From 2d0e841fdffb93628433fbffab779a1ee60d5531 Mon Sep 17 00:00:00 2001 From: "jinhyung.jo" Date: Thu, 13 Mar 2014 18:48:28 +0900 Subject: [PATCH] maru_overlay : Add new overlay device for Tizen emulator Add new overlay device for Tizen emulator Change-Id: I0bcb64e69d6152b9b1bf3cac1322ab840970ec6c Signed-off-by: Jinhyung Jo --- arch/x86/configs/i386_tizen_emul_defconfig | 14 +- drivers/maru/Kconfig | 4 + drivers/maru/Makefile | 1 + drivers/maru/maru_overlay.c | 565 +++++++++++++++++++++ include/linux/pci_ids.h | 1 + 5 files changed, 583 insertions(+), 2 deletions(-) create mode 100644 drivers/maru/maru_overlay.c diff --git a/arch/x86/configs/i386_tizen_emul_defconfig b/arch/x86/configs/i386_tizen_emul_defconfig index e037f98c7534..7f6d958ef74b 100644 --- a/arch/x86/configs/i386_tizen_emul_defconfig +++ b/arch/x86/configs/i386_tizen_emul_defconfig @@ -2035,11 +2035,15 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia core support # -# CONFIG_MEDIA_CAMERA_SUPPORT is not set +CONFIG_MEDIA_CAMERA_SUPPORT=y # CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set # CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set # CONFIG_MEDIA_RADIO_SUPPORT is not set # CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +CONFIG_VIDEO_DEV=y +# CONFIG_VIDEO_V4L2_SUBDEV_API is not set +CONFIG_VIDEO_V4L2=y # CONFIG_VIDEO_ADV_DEBUG is not set # CONFIG_VIDEO_FIXED_MINOR_RANGES is not set # CONFIG_TTPCI_EEPROM is not set @@ -2048,7 +2052,12 @@ CONFIG_MEDIA_SUPPORT=y # Media drivers # # CONFIG_MEDIA_USB_SUPPORT is not set -# CONFIG_MEDIA_PCI_SUPPORT is not set +CONFIG_MEDIA_PCI_SUPPORT=y + +# +# Media capture support +# +CONFIG_V4L_PLATFORM_DRIVERS=y # # Supported MMC/SDIO adapters @@ -2816,6 +2825,7 @@ CONFIG_IOMMU_SUPPORT=y CONFIG_MARU=y CONFIG_MARU_VIRTIO_TOUCHSCREEN=y CONFIG_MARU_BACKLIGHT=y +CONFIG_MARU_OVERLAY=y CONFIG_MARU_VIRTIO_HWKEY=y CONFIG_MARU_VIRTIO_KEYBOARD=y CONFIG_MARU_VIRTIO_NFC=y diff --git a/drivers/maru/Kconfig b/drivers/maru/Kconfig index 951eeb673f9a..3a60ae15c080 100644 --- a/drivers/maru/Kconfig +++ b/drivers/maru/Kconfig @@ -13,6 +13,10 @@ config MARU_BACKLIGHT help Say Y to enable the backlight driver of MARU. +config MARU_OVERLAY + tristate "MARU overlay Driver" + depends on MARU != n && VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + config MARU_VIRTIO_EVDI tristate "MARU VirtIO Emulator Virtual Device Interface Driver" depends on MARU != n diff --git a/drivers/maru/Makefile b/drivers/maru/Makefile index a8f7aaa75d1d..854b5b1a2277 100644 --- a/drivers/maru/Makefile +++ b/drivers/maru/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_MARU_VIRTIO_TOUCHSCREEN) += maru_virtio_touchscreen.o obj-$(CONFIG_MARU_BACKLIGHT) += maru_bl.o +obj-$(CONFIG_MARU_OVERLAY) += maru_overlay.o obj-$(CONFIG_MARU_VIRTIO_HWKEY) += maru_virtio_hwkey.o obj-$(CONFIG_MARU_VIRTIO_KEYBOARD) += maru_virtio_keyboard.o obj-$(CONFIG_MARU_VIRTIO_NFC) += maru_virtio_nfc.o diff --git a/drivers/maru/maru_overlay.c b/drivers/maru/maru_overlay.c new file mode 100644 index 000000000000..4bc8ebb6f5db --- /dev/null +++ b/drivers/maru/maru_overlay.c @@ -0,0 +1,565 @@ +/* + * Maru Virtual Overlay Driver + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: + * Jinhyung Jo + * Yeongkyoon Lee + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Contributors: + * - S-Core Co., Ltd + * + */ + +#include +#include +#include +#include +#include + +#define SVO_DRIVER_MAJORVERSION 0 +#define SVO_DRIVER_MINORVERSION 2 + +enum { + OVERLAY_POWER = 0x00, + OVERLAY_POSITION = 0x04, /* left & top */ + OVERLAY_SIZE = 0x08, /* width & height */ +}; + +DEFINE_PCI_DEVICE_TABLE(svo_pci_tbl) = { + { PCI_DEVICE(PCI_VENDOR_ID_TIZEN, PCI_DEVICE_ID_VIRTUAL_OVERLAY) }, + {} +}; +MODULE_DEVICE_TABLE(pci, svo_pci_tbl); + +struct svo { + /* pci device */ + struct pci_dev *pci_dev; + + /* video device parameters */ + struct video_device *video_dev0; + struct video_device *video_dev1; + + resource_size_t mem_start; + resource_size_t reg_start; + + resource_size_t mem_size; + resource_size_t reg_size; + + /* svo: memory mapped registers */ + unsigned char __iomem *svo_mmreg; + + /* set to 1 if the device is in use */ + unsigned long in_use0; + unsigned long in_use1; + + /* overlaid rect */ + struct v4l2_rect w0, w1; +}; + +/* + * driver structure - only one possible + */ +static struct svo svo; + +/* + * virtual register access helper + */ +static void overlay_power(int num, int onoff) +{ + unsigned int ret; + + ret = readl(svo.svo_mmreg + num * svo.reg_size / 2 + OVERLAY_POWER); + if (ret != onoff) { + writel(onoff, svo.svo_mmreg + + num * svo.reg_size / 2 + OVERLAY_POWER); + } +} + +/* + * svo ioctls + */ +static int svo_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "svo"); + strcpy(cap->card, "svo"); + sprintf(cap->bus_info, "PCI:%s", pci_name(svo.pci_dev)); + + cap->version = (SVO_DRIVER_MAJORVERSION << 8) + + SVO_DRIVER_MINORVERSION; + + cap->capabilities = V4L2_CAP_VIDEO_OVERLAY; + + return 0; +} + +static int svo0_g_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int ret = 0; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + ret = readl(svo.svo_mmreg + OVERLAY_POSITION); + svo.w0.left = ret & 0xFFFF; + svo.w0.top = (ret >> 16) & 0xFFFF; + ret = readl(svo.svo_mmreg + OVERLAY_SIZE); + svo.w0.width = ret & 0xFFFF; + svo.w0.height = (ret >> 16) & 0xFFFF; + + f->fmt.win.w.left = svo.w0.left; + f->fmt.win.w.top = svo.w0.top; + f->fmt.win.w.width = svo.w0.width; + f->fmt.win.w.height = svo.w0.height; + + return 0; +} + +static int svo1_g_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int ret = 0; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + ret = readl(svo.svo_mmreg + svo.reg_size / 2 + OVERLAY_POSITION); + svo.w1.left = ret & 0xFFFF; + svo.w1.top = (ret >> 16) & 0xFFFF; + ret = readl(svo.svo_mmreg + svo.reg_size / 2 + OVERLAY_SIZE); + svo.w1.width = ret & 0xFFFF; + svo.w1.height = (ret >> 16) & 0xFFFF; + + f->fmt.win.w.left = svo.w1.left; + f->fmt.win.w.top = svo.w1.top; + f->fmt.win.w.width = svo.w1.width; + f->fmt.win.w.height = svo.w1.height; + + return 0; +} + +static int svo0_s_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + if (f->fmt.win.w.left < 0) { + return -EINVAL; + } + if (f->fmt.win.w.top < 0) { + return -EINVAL; + } + + if (f->fmt.win.w.width < 0) { + return -EINVAL; + } + if (f->fmt.win.w.height < 0) { + return -EINVAL; + } + + svo.w0.left = f->fmt.win.w.left; + svo.w0.top = f->fmt.win.w.top; + svo.w0.width = f->fmt.win.w.width; + svo.w0.height = f->fmt.win.w.height; + + arg = svo.w0.left | (svo.w0.top << 16); + writel(arg, svo.svo_mmreg + OVERLAY_POSITION); + arg = svo.w0.width | (svo.w0.height << 16); + writel(arg, svo.svo_mmreg + OVERLAY_SIZE); + + return 0; +} + +static int svo1_s_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + unsigned int arg; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + if (f->fmt.win.w.left < 0) { + return -EINVAL; + } + if (f->fmt.win.w.top < 0) { + return -EINVAL; + } + + if (f->fmt.win.w.width < 0) { + return -EINVAL; + } + if (f->fmt.win.w.height < 0) { + return -EINVAL; + } + + svo.w1.left = f->fmt.win.w.left; + svo.w1.top = f->fmt.win.w.top; + svo.w1.width = f->fmt.win.w.width; + svo.w1.height = f->fmt.win.w.height; + + arg = svo.w1.left | (svo.w1.top << 16); + writel(arg, svo.svo_mmreg + svo.reg_size / 2 + OVERLAY_POSITION); + arg = svo.w1.width | (svo.w1.height << 16); + writel(arg, svo.svo_mmreg + svo.reg_size / 2 + OVERLAY_SIZE); + + return 0; +} + +static int svo_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + if (p->count > 1) { + return -EINVAL; + } + + if (p->memory != V4L2_MEMORY_MMAP) { + return -EINVAL; + } + + return 0; +} + +static int svo0_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + if (p->index) { + return -EINVAL; + } + + p->length = svo.mem_size / 2; + p->m.offset = svo.mem_start; + + return 0; +} + +static int svo1_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + if (p->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) { + return -EINVAL; + } + + if (p->index) { + return -EINVAL; + } + + p->length = svo.mem_size / 2; + p->m.offset = svo.mem_start + p->length; + + return 0; +} + +static int svo0_overlay(struct file *file, void *fh, unsigned int i) +{ + overlay_power(0, i); + + return 0; +} + +static int svo1_overlay(struct file *file, void *fh, unsigned int i) +{ + overlay_power(1, i); + + return 0; +} + +/* + * File operations + */ + +static int svo0_open(struct file *file) +{ + if (test_and_set_bit(0, &svo.in_use0)) { + return -EBUSY; + } + + return 0; +} + +static int svo1_open(struct file *file) +{ + if (test_and_set_bit(0, &svo.in_use1)) { + return -EBUSY; + } + + return 0; +} + +static void svo_vm_open(struct vm_area_struct *vma) +{ +} + +static void svo_vm_close(struct vm_area_struct *vma) +{ +} + +static const struct vm_operations_struct svo_vm_ops = { + .open = svo_vm_open, + .close = svo_vm_close, +}; + +static int svo_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start; + + if (size > svo.mem_size) { + return -EINVAL; + } + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + size, PAGE_SHARED)) { + return -EAGAIN; + } + + vma->vm_ops = &svo_vm_ops; + vma->vm_flags &= ~VM_IO; /* not I/O memory */ + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; /* avoid to swap out this VMA */ + + return 0; +} + +static int svo0_release(struct file *file) +{ + clear_bit(0, &svo.in_use0); + overlay_power(0, 0); + + return 0; +} + +static int svo1_release(struct file *file) +{ + clear_bit(0, &svo.in_use1); + overlay_power(1, 0); + + return 0; +} + +static const struct v4l2_ioctl_ops svo0_ioctl_ops = { + .vidioc_querycap = svo_querycap, + .vidioc_g_fmt_vid_overlay = svo0_g_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = svo0_s_fmt_vid_overlay, + .vidioc_reqbufs = svo_reqbufs, + .vidioc_querybuf = svo0_querybuf, + .vidioc_overlay = svo0_overlay, +}; + +static const struct v4l2_ioctl_ops svo1_ioctl_ops = { + .vidioc_querycap = svo_querycap, + .vidioc_g_fmt_vid_overlay = svo1_g_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = svo1_s_fmt_vid_overlay, + .vidioc_reqbufs = svo_reqbufs, + .vidioc_querybuf = svo1_querybuf, + .vidioc_overlay = svo1_overlay, +}; + +static const struct v4l2_file_operations svo0_fops = { + .owner = THIS_MODULE, + .open = svo0_open, + .release = svo0_release, + .mmap = svo_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_file_operations svo1_fops = { + .owner = THIS_MODULE, + .open = svo1_open, + .release = svo1_release, + .mmap = svo_mmap, + .ioctl = video_ioctl2, +}; + +static struct video_device svo0_template = { + .name = "svo0", + .fops = &svo0_fops, + .ioctl_ops = &svo0_ioctl_ops, + .release = video_device_release, + .minor = -1, +}; + +static struct video_device svo1_template = { + .name = "svo1", + .fops = &svo1_fops, + .ioctl_ops = &svo1_ioctl_ops, + .release = video_device_release, + .minor = -1, +}; + +static int svo_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + int ret = -EBUSY; + + if (svo.pci_dev != NULL) { + printk(KERN_ERR "svo: only one device allowed!\n"); + return ret; + } + + ret = -ENOMEM; + svo.pci_dev = pci_dev; + svo.video_dev0 = video_device_alloc(); + if (!svo.video_dev0) { + printk(KERN_ERR "svo0: video_device_alloc() failed!\n"); + return ret; + } + + svo.video_dev1 = video_device_alloc(); + if (!svo.video_dev1) { + printk(KERN_ERR "svo1: video_device_alloc() failed!\n"); + video_device_release(svo.video_dev0); + return ret; + } + + memcpy(svo.video_dev0, &svo0_template, sizeof(svo0_template)); + svo.video_dev0->dev_parent = &svo.pci_dev->dev; + memcpy(svo.video_dev1, &svo1_template, sizeof(svo1_template)); + svo.video_dev1->dev_parent = &svo.pci_dev->dev; + + ret = -EIO; + + ret = pci_enable_device(svo.pci_dev); + if (ret) { + printk(KERN_ERR "svo: pci_enable_device failed\n"); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + + svo.mem_start = pci_resource_start(svo.pci_dev, 0); + svo.mem_size = pci_resource_len(svo.pci_dev, 0); + if (!svo.mem_start) { + printk(KERN_ERR "svo: svo has no device base address\n"); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + if (!request_mem_region(svo.mem_start, svo.mem_size, "svo")) { + printk(KERN_ERR "svo: request_mem_region failed\n"); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + + svo.reg_start = pci_resource_start(svo.pci_dev, 1); + svo.reg_size = pci_resource_len(svo.pci_dev, 1); + if (!svo.reg_start) { + printk(KERN_ERR "svo: svo has no device base address\n"); + release_mem_region(svo.mem_start, svo.mem_size); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + if (!request_mem_region(svo.reg_start, svo.reg_size, "svo")) { + printk(KERN_ERR "svo: request_mem_region failed\n"); + release_mem_region(svo.mem_start, svo.mem_size); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + + svo.svo_mmreg = ioremap(svo.reg_start, svo.reg_size); + if (!svo.svo_mmreg) { + printk(KERN_ERR "svo: ioremap failed\n"); + release_mem_region(svo.reg_start, svo.reg_size); + release_mem_region(svo.mem_start, svo.mem_size); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + + pci_write_config_byte(svo.pci_dev, PCI_CACHE_LINE_SIZE, 8); + pci_write_config_byte(svo.pci_dev, PCI_LATENCY_TIMER, 64); + + pci_set_master(svo.pci_dev); + + /* register number is set to force + * because of the camera device (/dev/video0) + */ + if (video_register_device(svo.video_dev0, VFL_TYPE_GRABBER, + 1) < 0) { /* set to /dev/video1 */ + printk(KERN_ERR "svo: video_register_device failed\n"); + iounmap(svo.svo_mmreg); + release_mem_region(svo.reg_start, svo.reg_size); + release_mem_region(svo.mem_start, svo.mem_size); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + if (video_register_device(svo.video_dev1, VFL_TYPE_GRABBER, + 2) < 0) { /* set to /dev/video2 */ + printk(KERN_ERR "svo: video_register_device failed\n"); + video_unregister_device(svo.video_dev0); + iounmap(svo.svo_mmreg); + release_mem_region(svo.reg_start, svo.reg_size); + release_mem_region(svo.mem_start, svo.mem_size); + pci_disable_device(svo.pci_dev); + video_device_release(svo.video_dev0); + video_device_release(svo.video_dev1); + return ret; + } + + printk(KERN_INFO "svo: MARU Virtual Overlay Driver v%d.%d is loaded\n", + SVO_DRIVER_MAJORVERSION, SVO_DRIVER_MINORVERSION); + + return 0; +} + +static struct pci_driver svo_pci_driver = { + .name = "svo", + .id_table = svo_pci_tbl, + .probe = svo_initdev, +/* .remove = __devexit_p(cx8800_finidev), */ +#ifdef CONFIG_PM +/* .suspend = cx8800_suspend, */ +/* .resume = cx8800_resume, */ +#endif +}; + +static int __init svo_init(void) +{ + return pci_register_driver(&svo_pci_driver); +} + +static void __exit svo_fini(void) +{ + pci_unregister_driver(&svo_pci_driver); +} + +module_init(svo_init); +module_exit(svo_fini); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d7376e958f1e..5f7d0d14573e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2092,6 +2092,7 @@ #define PCI_VENDOR_ID_SAMSUNG 0x144d #define PCI_VENDOR_ID_TIZEN 0xC9B5 +#define PCI_DEVICE_ID_VIRTUAL_OVERLAY 0x1010 #define PCI_DEVICE_ID_VIRTUAL_BRIGHTNESS 0x1014 #define PCI_VENDOR_ID_GIGABYTE 0x1458 -- 2.34.1