TV: add EXTERNAL_INPUT
authorYunHo Lee <yun117.lee@samsung.com>
Thu, 21 Jul 2016 04:25:28 +0000 (13:25 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Tue, 26 Jul 2016 02:17:20 +0000 (11:17 +0900)
Change-Id: I0bc94f28d2b9dca6bd797582541a3c2e0986d178
Signed-off-by: YunHo Lee <yun117.lee@samsung.com>
arch/x86/configs/tizen_emul_defconfig
drivers/maru/Kconfig
drivers/maru/tv/Makefile
drivers/maru/tv/maru_component.c [new file with mode: 0644]
drivers/maru/tv/maru_composite.c [new file with mode: 0644]
drivers/maru/tv/maru_external_input.h [new file with mode: 0644]
drivers/maru/tv/maru_hdmi.c [new file with mode: 0644]
drivers/maru/tv/maru_hdmiswitch.c [new file with mode: 0644]
include/linux/pci_ids.h

index 2644db9..a306c1d 100644 (file)
@@ -3632,6 +3632,7 @@ CONFIG_MARU_VDPRAM=y
 CONFIG_MARU_VIRTIO_VMODEM=y
 CONFIG_MARU_VIRTIO_ROTARY=y
 CONFIG_MARU_TV=y
+CONFIG_MARU_EXTERNAL_INPUT=y
 CONFIG_MARU_TV_TUNER=y
 CONFIG_MARU_TV_EEPROM=y
 CONFIG_MARU_TV_DUMMY=y
index 5bf0d4d..d2d0a3b 100644 (file)
@@ -71,6 +71,11 @@ config MARU_TV
        depends on MARU != n
        default n
 
+config MARU_EXTERNAL_INPUT
+       tristate "MARU External Input Driver"
+       depends on MARU_TV != n && VIDEO_DEV && VIDEO_V4L2
+       select VIDEOBUF_VMALLOC
+
 config MARU_TV_TUNER
        tristate "MARU Tuner Driver"
        depends on MARU_TV != n && DVB_CORE && PCI
index ff6c799..aa42be7 100644 (file)
@@ -7,4 +7,5 @@ obj-$(CONFIG_MARU_TV_GPIO) += maru_dummy_gpio.o
 obj-$(CONFIG_MARU_TV_I2C_SDP) += maru_dummy_i2c_sdp.o
 obj-$(CONFIG_MARU_TV_MICOM_MSG) += maru_dummy_micom_msg.o
 obj-$(CONFIG_MARU_TV_MICOM_ISP) += maru_dummy_micom_isp.o
+obj-$(CONFIG_MARU_EXTERNAL_INPUT) += maru_hdmiswitch.o maru_hdmi.o maru_composite.o maru_component.o
 obj-$(CONFIG_MARU_TV_TUNER) += maru_tuner.o maru_dmxdev.o maru_dtv_decoder.o maru_atv.o maru_sif.o
diff --git a/drivers/maru/tv/maru_component.c b/drivers/maru/tv/maru_component.c
new file mode 100644 (file)
index 0000000..07c7dde
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * MARU Virtual Component Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Hyunjin Lee <hyunjin816.lee@samsung.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "maru_v4l2.h"
+#include "maru_external_input.h"
+
+static int extinput_component_debug = 0;
+module_param(extinput_component_debug, int, 0644);
+MODULE_PARM_DESC(extinput_component_debug, "Turn on/off maru external input component debugging (default:off).");
+
+#define maru_component_err(fmt, arg...) \
+       printk(KERN_ERR "[ERR]maru_component[%s]: " fmt, __func__, ##arg)
+
+#define maru_component_warn(fmt, arg...) \
+       printk(KERN_WARNING "[WARN]maru_component[%s]: " fmt, __func__, ##arg)
+
+#define maru_component_info(fmt, arg...) \
+       printk(KERN_INFO "[INFO]maru_component[%s]: " fmt, __func__, ##arg)
+
+#define maru_component_dbg(level, fmt, arg...) \
+       do { \
+               if (extinput_component_debug >= (level)) { \
+                       printk(KERN_INFO "[DBG]maru_component[%s]: " fmt, \
+                               __func__, ##arg); \
+               } \
+       } while (0)
+
+enum maru_component_opstate {
+       S_IDLE = 0,
+       S_RUNNING = 1
+};
+
+struct maru_component_device {
+       struct mutex                    *mlock;
+       void __iomem                    *mmregs;
+       enum maru_component_opstate     opstate;
+
+       int source_type;
+       int platform_type;
+       struct v4l2_fake_res fake_comp;
+
+       struct v4l2_pix_ext_format pix_fmt_after_lookup;
+};
+
+static struct maru_component_device *dev = NULL;
+static struct video_device *vfd_component = NULL;
+
+#define MARU_COMPONENT_MODULE_NAME "maru_component"
+#define COMPONENT_DEVICE 24
+#define DFL_WIDTH      1920
+#define DFL_HEIGHT     1080
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+       maru_component_dbg(5, "[component]enter\n");
+       strcpy(cap->driver, MARU_COMPONENT_MODULE_NAME);
+       return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+       int ret = 0;
+       struct maru_component_device *dev = priv;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       if (ctrl->id != V4L2_CID_DV_VIDEO_OUTPUT)
+               return ret;
+
+       mutex_lock(dev->mlock);
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(ctrl->id, dev->mmregs + MARU_EXTINPUT_S_DATA);
+       iowrite32(ctrl->value, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_COMPONENT_S_CTRL);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_S_CTRL);
+       if (ret) {
+               maru_component_err("Failed to set the control value\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
+{
+       maru_component_dbg(5, "[component]enter\n");
+       return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_component_device *dev = priv;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_IDLE) {
+               maru_component_err("The device state is not S_IDLE\n");
+               mutex_unlock(dev->mlock);
+               return -EBUSY;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_COMPONENT_START_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_START_PREVIEW);
+       if (ret) {
+               maru_component_err("Failed to start preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->fake_comp.flag = 0;
+       dev->opstate = S_RUNNING;
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_component_device *dev = priv;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_RUNNING) {
+               maru_component_err("The device state is not S_RUNNING. Do nothing\n");
+               mutex_unlock(dev->mlock);
+               return ret;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_COMPONENT_STOP_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_STOP_PREVIEW);
+       if (ret) {
+               maru_component_err("Failed to stop preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+    }
+
+       dev->opstate = S_IDLE;
+       if (ret) {
+               maru_component_err("Failed to stream off the video buffer: %d\n", ret);
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp)
+{
+       /* TODO: always true. check again later */
+       inp->status = true;
+
+       return 0;
+}
+
+/*
+ * vidioc_g_ext_ctrls, vidioc_s_ext_ctrls code base on hdmi.c
+ */
+static int vidioc_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls)
+{
+       int ret = 0;
+       struct maru_component_device *dev = priv;
+       int i = 0;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       mutex_lock(dev->mlock);
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_DV) {
+               struct v4l2_ext_control *ctrl;
+               struct v4l2_pix_ext_format *pextfmt;
+               struct v4l2_fake_res *fake;
+
+               for (i = 0; i < ctrls->count; i++) {
+                       ctrl = ctrls->controls + i;
+                       switch (ctrl->id) {
+                       case V4L2_CID_DV_S_RESOLUTION:
+                               if (dev->fake_comp.flag == 1) {
+                                       dev->pix_fmt_after_lookup.hres = 1280;
+                                       dev->pix_fmt_after_lookup.vres = 720;
+                                       dev->pix_fmt_after_lookup.hstart = 0;
+                                       dev->pix_fmt_after_lookup.vstart = 0;
+                                       dev->pix_fmt_after_lookup.htotal = 1650;
+                                       dev->pix_fmt_after_lookup.vtotal = 750;
+                                       dev->pix_fmt_after_lookup.vfreq = 6000;
+                                       dev->pix_fmt_after_lookup.scantype = 1;
+                       } else {
+                                       pextfmt = (struct v4l2_pix_ext_format*)ctrl->string;
+                                       if((pextfmt->hres) && (pextfmt->vres)) {
+
+                                               iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+                                               iowrite32(pextfmt->hres, dev->mmregs + MARU_EXTINPUT_S_DATA);
+                                               iowrite32(pextfmt->vres, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+                                               iowrite32(0, dev->mmregs + MARU_COMPONENT_S_EXT_CTRLS);
+                                               ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_S_EXT_CTRLS);
+                                               if (ret) {
+                                                       maru_component_err("Failed to set the resolution: %d\n", -ret);
+                                                       ret = -ret;
+                                                       goto err;
+                                               }
+
+                                               dev->pix_fmt_after_lookup.hres = pextfmt->hres;
+                                               dev->pix_fmt_after_lookup.vres = pextfmt->vres;
+                                               dev->pix_fmt_after_lookup.hstart = pextfmt->hstart;
+                                               dev->pix_fmt_after_lookup.vstart = pextfmt->vstart;
+                                               dev->pix_fmt_after_lookup.htotal = pextfmt->htotal;
+                                               dev->pix_fmt_after_lookup.vtotal = pextfmt->vtotal;
+                                               dev->pix_fmt_after_lookup.hfreq = pextfmt->hfreq;
+                                               dev->pix_fmt_after_lookup.vfreq = pextfmt->vfreq;
+                                               dev->pix_fmt_after_lookup.scantype = pextfmt->scantype;
+                                       } else {
+                                               maru_component_err("Resolution not set %d x %d\n", pextfmt->hres, pextfmt->vres);
+                                               ret = -EINVAL;
+                                               goto err;
+                                       }
+                               }
+                               break;
+                       case V4L2_CID_DV_S_FAKE_RESOLUTION:
+                               fake = (struct v4l2_fake_res *)ctrl->string;
+                               dev->fake_comp.flag = fake->flag;
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+       }
+
+err:
+       mutex_unlock(dev->mlock);
+
+       return ret;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                                                               struct v4l2_ext_controls *ctrls)
+{
+       int ret = 0;
+       struct maru_component_device *dev = priv;
+       struct v4l2_ext_control *ctrl = NULL;
+       struct v4l2_pix_ext_format extfmt;
+       struct v4l2_drm drm;
+       int i = 0;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_DV)
+       return ret;
+
+       mutex_lock(dev->mlock);
+
+       for (i = 0; i < ctrls->count; i++) {
+               ctrl = ctrls->controls + i;
+               switch (ctrl->id) {
+               case V4L2_CID_DV_G_RESOLUTION:
+                       if (dev->fake_comp.flag == 1) {
+                               extfmt.hres = 1280;
+                               extfmt.vres = 720;
+                               extfmt.hstart = 0;
+                               extfmt.vstart = 0;
+                               extfmt.htotal = 1650;
+                               extfmt.vtotal = 750;
+                               extfmt.hfreq = 45000;
+                               extfmt.vfreq = 6000;
+                               extfmt.scantype = 1;
+                       } else {
+                               iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+
+                               iowrite32(0, dev->mmregs + MARU_COMPONENT_G_EXT_CTRLS);
+                               ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_G_EXT_CTRLS);
+                               if (ret) {
+                                       maru_component_err("Failed to get the resolution: %d\n", -ret);
+                                       mutex_unlock(dev->mlock);
+                                       return -ret;
+                               }
+
+                               /* FIXME : Suppose that TV resolution is 1080I */
+                               extfmt.hsyncpositive = 1;
+                               extfmt.vsyncpositive = 1;
+                               extfmt.hres = ioread32(dev->mmregs + MARU_EXTINPUT_G_DATA);
+                               extfmt.vres = ioread32(dev->mmregs + MARU_EXTINPUT_G_DATA);
+                               extfmt.hstart = 0;
+                               extfmt.vstart = 0;
+                               extfmt.htotal = 2200;
+                               extfmt.vtotal = 562;
+                               extfmt.hfreq = 33750;
+                               extfmt.vfreq = 6000;
+                               extfmt.scantype = 1;
+                       }
+
+                       if (copy_to_user(ctrl->string, (char *)&extfmt, sizeof(struct v4l2_pix_ext_format))) {
+                               maru_component_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+
+                       break;
+
+               case V4L2_CID_DV_G_SIGNAL_INFO:
+                       memset(&drm , 0, sizeof(drm));
+                       drm.u.extin_info.htotal = dev->pix_fmt_after_lookup.htotal;
+                       drm.u.extin_info.vtotal = dev->pix_fmt_after_lookup.vtotal;
+                       drm.u.extin_info.hactive = dev->pix_fmt_after_lookup.hstart;
+                       drm.u.extin_info.vactive = dev->pix_fmt_after_lookup.vstart;
+                       drm.u.extin_info.hactive_size = dev->pix_fmt_after_lookup.hres;
+                       drm.u.extin_info.vactive_size = dev->pix_fmt_after_lookup.vres;
+                       drm.u.extin_info.scantype = dev->pix_fmt_after_lookup.scantype;
+
+                       if(dev->pix_fmt_after_lookup.vfreq > 2300 && dev->pix_fmt_after_lookup.vfreq < 2500)
+                               drm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_24HZ;
+                       if(dev->pix_fmt_after_lookup.vfreq > 2900 && dev->pix_fmt_after_lookup.vfreq < 3100)
+                               drm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_30HZ;
+                       if(dev->pix_fmt_after_lookup.vfreq > 4900 && dev->pix_fmt_after_lookup.vfreq < 5100)
+                               drm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_50HZ;
+                       if(dev->pix_fmt_after_lookup.vfreq > 5900 && dev->pix_fmt_after_lookup.vfreq < 6100)
+                               drm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_60HZ;
+
+                       drm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_YUV444;
+                       drm.u.extin_info.syncmode = 1; // 0: HDMI or DTV, 1 otherwise
+                       drm.u.extin_info.pcinput = 0;
+                       drm.inputport = V4L2_DRM_INPUTPORT_TYPE_COMPONENT;
+                       drm.reserved = 0x1234ABCD;
+
+                       if(copy_to_user(ctrl->string, (char *)&drm, sizeof(drm))) {
+                               maru_component_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+               default:
+                       ret = -ENOENT;
+                       goto err;
+                       break;
+               }
+       }
+err:
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+/* v4l2_ioctl_ops for COMPONENT */
+struct v4l2_ioctl_ops maru_component_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap, //vidioc_querycap,
+       .vidioc_s_ctrl = vidioc_s_ctrl,
+       .vidioc_streamon = vidioc_streamon,
+       .vidioc_streamoff = vidioc_streamoff,
+       .vidioc_enum_input = vidioc_enum_input,
+       .vidioc_s_input = vidioc_s_input,
+       .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls
+
+};
+
+const struct v4l2_ioctl_ops *get_component_v4l2_ioctl_ops(void)
+{
+       return &maru_component_ioctl_ops;
+}
+
+/* ------------------------------------------------------------------
+       File operations for the device
+       ------------------------------------------------------------------*/
+static int maru_component_open(struct file *file)
+{
+       int ret = 0;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       file->private_data = dev;
+
+       mutex_lock(dev->mlock);
+
+       iowrite32(0, dev->mmregs + MARU_COMPONENT_OPEN);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_OPEN);
+       if (ret) {
+               maru_component_err("Failed to open the device\n");
+               mutex_unlock(dev->mlock);
+       return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+
+       return ret;
+
+}
+
+static int maru_component_close(struct file *file)
+{
+       int ret = 0;
+       struct maru_component_device *dev = file->private_data;
+
+       maru_component_dbg(5, "[component]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate == S_RUNNING) {
+               maru_component_err("The device has been terminated unexpectedly.\n");
+               iowrite32(1, dev->mmregs + MARU_COMPONENT_STOP_PREVIEW);
+               ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_STOP_PREVIEW);
+               if (ret) {
+                       maru_component_err("Failed to stop the preview\n");
+                       mutex_unlock(dev->mlock);
+                       return -ret;
+               }
+
+               dev->opstate = S_IDLE;
+       }
+
+       iowrite32(0, dev->mmregs + MARU_COMPONENT_CLOSE);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPONENT_CLOSE);
+       if (ret) {
+               maru_component_err("Failed to close the device\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+
+       return ret;
+}
+
+static const struct v4l2_file_operations maru_component_fops = {
+       .owner = THIS_MODULE,
+       .open = maru_component_open,
+       .release = maru_component_close,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+int maru_component_init(struct maru_extinput_device *device)
+{
+       int ret = 0;
+
+       dev = kzalloc(sizeof(struct maru_component_device), GFP_KERNEL);
+       if (!dev) {
+               maru_component_err("kzalloc() failed\n");
+               ret = -ENOMEM;
+               goto enomem;
+       }
+
+       dev->opstate = S_IDLE;
+       dev->mmregs = device->mmregs;
+       dev->mlock = &device->mlock;
+
+       dev->platform_type = (int)ioread32(dev->mmregs + MARU_EXTINPUT_PLATFORM_TYPE);
+       maru_component_info("platform type: %s\n", dev->platform_type ? "hawk" : "golf");
+
+
+       vfd_component = video_device_alloc();
+       if (!vfd_component) {
+               maru_component_err("failed to allocate component video device\n");
+               ret = -ENOMEM;
+               goto video_device_alloc_failed;
+       }
+
+       vfd_component->fops = &maru_component_fops;
+       vfd_component->ioctl_ops = get_component_v4l2_ioctl_ops();
+       vfd_component->release = video_device_release;
+       strcpy(vfd_component->name, "maru_component");
+       vfd_component->dev_parent = &device->pdev->dev;
+       vfd_component->v4l2_dev = &device->v4l2_dev;
+
+       ret = video_register_device(vfd_component, VFL_TYPE_GRABBER, COMPONENT_DEVICE);
+       if (ret) {
+               maru_component_err("failed to register component video device\n");
+               goto video_register_device_failed;
+       }
+
+       return ret;
+
+video_register_device_failed:
+       video_device_release(vfd_component);
+       vfd_component = NULL;
+video_device_alloc_failed:
+       kfree(dev);
+enomem:
+       return ret;
+}
+
+void maru_component_release(void)
+{
+       if (vfd_component) {
+               video_unregister_device(vfd_component);
+               video_device_release(vfd_component);
+       }
+
+       if (dev)
+               kfree(dev);
+
+       vfd_component = NULL;
+       dev = NULL;
+}
diff --git a/drivers/maru/tv/maru_composite.c b/drivers/maru/tv/maru_composite.c
new file mode 100644 (file)
index 0000000..bc4703c
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * MARU Virtual Composite Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Hyunjin Lee <hyunjin816.lee@samsung.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "maru_v4l2.h"
+#include "maru_external_input.h"
+#include "maru_tuner.h"
+
+static int extinput_composite_debug = 0;
+module_param(extinput_composite_debug, int, 0644);
+MODULE_PARM_DESC(extinput_composite_debug, "Turn on/off maru external input composite debugging (default:off).");
+
+#define maru_composite_err(fmt, arg...) \
+       printk(KERN_ERR "[ERR]maru_composite[%s]: " fmt, __func__, ##arg)
+
+#define maru_composite_warn(fmt, arg...) \
+       printk(KERN_WARNING "[WARN]maru_composite[%s]: " fmt, __func__, ##arg)
+
+#define maru_composite_info(fmt, arg...) \
+       printk(KERN_INFO "[INFO]maru_composite[%s]: " fmt, __func__, ##arg)
+
+#define maru_composite_dbg(level, fmt, arg...) \
+       do { \
+               if (extinput_composite_debug >= (level)) { \
+                       printk(KERN_INFO "[DBG]maru_composite[%s]: " fmt, \
+                                       __func__, ##arg); \
+               } \
+       } while (0)
+
+enum maru_composite_opstate {
+       S_IDLE = 0,
+       S_RUNNING = 1
+};
+
+struct maru_composite_device {
+       struct mutex                    *mlock;
+       void __iomem                    *mmregs;
+       enum maru_composite_opstate     opstate;
+
+    int platform_type;
+       int source_type;
+       int atv_lock_status;
+       struct v4l2_fake_res fake_av;
+
+       struct v4l2_pix_ext_format pix_fmt_after_lookup;
+};
+
+static struct maru_composite_device *dev = NULL;
+static struct video_device *vfd_composite = NULL;
+
+#define MARU_COMPOSITE_MODULE_NAME "maru_composite"
+#define COMPOSITE_DEVICE 20
+#define DFL_WIDTH      1920
+#define DFL_HEIGHT     1080
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       strcpy(cap->driver, MARU_COMPOSITE_MODULE_NAME);
+
+       return 0;
+}
+
+static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+       struct maru_composite_device *dev = priv;
+       int type = dev->source_type;
+
+       if (dev->fake_av.flag == 1) {
+               ctrl->value = 1;
+       } else {
+               switch (ctrl->id) {
+               case V4L2_CID_SYNC_LOCK:
+               case V4L2_CID_AUTO_PRGM_LOCK:
+                       if (type == ATV) {
+                               int ret = 0;
+                               ctrl->value = maruatv_get_status();
+                               mutex_lock(dev->mlock);
+                               if (dev->atv_lock_status != ctrl->value) {
+                                       dev->atv_lock_status = ctrl->value;
+
+                                       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+                                       iowrite32(ctrl->value, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+                                       iowrite32(0, dev->mmregs + MARU_ATV_LOCK_STATUS);
+                                       ret = (int)ioread32(dev->mmregs + MARU_ATV_LOCK_STATUS);
+                                       if (ret) {
+                                               maru_composite_err("Failed to set ATV lock status\n");
+                                               mutex_unlock(dev->mlock);
+                                               return -ret;
+                                       }
+                               }
+                               mutex_unlock(dev->mlock);
+                       } else {
+                               /* TODO: always true. check again later */
+                               ctrl->value = true;
+                       }
+                       maru_composite_dbg(5, "lock ctrl->value= %d", ctrl->value);
+                       break;
+               case V4L2_CID_CGMS:
+               case V4L2_CID_NOISE:
+               case V4L2_CID_50HZ:
+               case V4L2_CID_STANDARD:
+               case V4L2_CID_SCART:
+               case V4L2_CID_DETECT_BURST:
+               case V4L2_CID_PAL_SWING:
+               default:
+                       maru_composite_dbg(5, "vidioc_g_ctrl id : %d\n", ctrl->id);
+                       break;
+               }
+
+       }
+       return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                               struct v4l2_control *ctrl)
+{
+       int ret = 0;
+       struct maru_composite_device *dev = priv;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       mutex_lock(dev->mlock);
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(ctrl->id, dev->mmregs + MARU_EXTINPUT_S_DATA);
+       iowrite32(ctrl->value, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_COMPOSITE_S_CTRL);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_S_CTRL);
+       if (ret) {
+               maru_composite_err("Failed to set the control value\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
+{
+       int ret = 0;
+       struct maru_composite_device *dev = priv;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       mutex_lock(dev->mlock);
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(input, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_COMPOSITE_S_INPUT);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_S_INPUT);
+       if (ret) {
+               maru_composite_err("Failed to set the control value\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+       dev->source_type = input;
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_composite_device *dev = priv;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_IDLE) {
+               maru_composite_err("The device state is not S_IDLE\n");
+               mutex_unlock(dev->mlock);
+               return -EBUSY;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_COMPOSITE_START_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_START_PREVIEW);
+       if (ret) {
+               maru_composite_err("Failed to start preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->fake_av.flag = 0;
+       dev->fake_av.analog_fake_res = 0;
+       dev->opstate = S_RUNNING;
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_composite_device *dev = priv;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_RUNNING) {
+               maru_composite_err("The device state is not S_RUNNING. Do nothing\n");
+               mutex_unlock(dev->mlock);
+               return ret;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_COMPOSITE_STOP_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_STOP_PREVIEW);
+       if (ret) {
+               maru_composite_err("Failed to stop preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->opstate = S_IDLE;
+       if (ret) {
+               maru_composite_err("Failed to stream off the video buffer: %d\n", ret);
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       *std = V4L2_STD_NTSC;
+
+       return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       return 0;
+}
+
+/*
+ * vidioc_g_ext_ctrls, vidioc_s_ext_ctrls code base on hdmi.c
+ */
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *ctrls)
+{
+       struct maru_composite_device *dev = priv;
+       struct v4l2_ext_control *ctrl = NULL;
+       struct v4l2_pix_ext_format extfmt;
+       struct v4l2_drm pdrm;
+       int i = 0;
+       int ret = 0;
+       int b50Hz = 0; /* FIXME: Suppose that frequency Hz is not 50 */
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_DV)
+               return ret;
+
+       mutex_lock(dev->mlock);
+       for (i = 0; i < ctrls->count; i++) {
+               ctrl = ctrls->controls + i;
+        switch (ctrl->id) {
+               case V4L2_CID_DV_G_RESOLUTION:
+                       if(dev->fake_av.flag) {
+                               if(dev->fake_av.analog_fake_res) {
+                                       extfmt.hres = 720;
+                                       extfmt.vres = 480;
+                                       extfmt.hstart = 0;
+                                       extfmt.vstart = 0;
+                                       extfmt.htotal = 858;
+                                       extfmt.vtotal = 525;
+                                       extfmt.hfreq = 15734;
+                                       extfmt.vfreq = 6000;
+                                       extfmt.scantype = false;
+                               } else {
+                                       extfmt.hres = 720;
+                                       extfmt.vres = 576;
+                                       extfmt.hstart = 0;
+                                       extfmt.vstart = 0;
+                                       extfmt.htotal = 864;
+                                       extfmt.vtotal = 625;
+                                       extfmt.hfreq = 15625;
+                                       extfmt.vfreq = 5000;
+                                       extfmt.scantype = false;
+                               }
+                       } else {
+                               /* get frequency status, but we don't get this status.
+                                  Suppose that frequency is not 50Hz
+                                  spIError = spIAvd_GetStatus(dev,  AVD_STATUS_50HZ, &sAvdStatus);
+                               */
+                               if (b50Hz == 1) {
+                                       extfmt.hres = 720;
+                                       extfmt.vres = 576;
+                                       extfmt.hstart = 133;
+                                       extfmt.vstart = 17;
+                                       extfmt.htotal = 1728;
+                                       extfmt.vtotal = 625;
+                                       extfmt.hfreq = 15625;
+                                       extfmt.vfreq = 5000;
+                                       extfmt.scantype = false;
+                               } else {
+                                       extfmt.hres = 720;
+                                       extfmt.vres = 480;
+                                       extfmt.hstart = 133;
+                                       extfmt.vstart = 13;
+                                       extfmt.htotal = 1716;
+                                       extfmt.vtotal = 525;
+                                       extfmt.hfreq = 15734;
+                                       extfmt.vfreq = 6000;
+                                       extfmt.scantype = false;
+                               }
+                       }
+                       maru_composite_dbg(5, "[composite] g_ext_ctrls(res_info) : hres = %d, vres = %d, hstart = %d," \
+                                       "vstart = %d, htotal = %d, vtotal = %d, vfreq = %d, scantype = %d\n",
+                                       extfmt.hres, extfmt.vres, extfmt.hstart, extfmt.vstart,
+                                       extfmt.htotal, extfmt.vtotal, extfmt.vfreq, extfmt.scantype);
+
+                       if(copy_to_user(ctrl->string, (char *)&extfmt, sizeof(extfmt))) {
+                               maru_composite_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+
+               case V4L2_CID_DV_G_SIGNAL_INFO:
+                       //pdrm = (struct v4l2_drm *)ctrl->string;
+                       if(dev->fake_av.flag) {
+                               if(dev->fake_av.analog_fake_res) {
+                                       pdrm.u.extin_info.htotal = 858;
+                                       pdrm.u.extin_info.vtotal = 525;
+                                       pdrm.u.extin_info.hactive = 0;
+                                       pdrm.u.extin_info.vactive = 0;
+                                       pdrm.u.extin_info.hactive_size = 720;
+                                       pdrm.u.extin_info.vactive_size = 480;
+                                       pdrm.u.extin_info.scantype = false;
+                                       pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_60HZ;
+                                       pdrm.u.extin_info.ntscpal_type = 1;
+                               } else {
+                                       pdrm.u.extin_info.htotal = 864;
+                                       pdrm.u.extin_info.vtotal = 625;
+                                       pdrm.u.extin_info.hactive = 0;
+                                       pdrm.u.extin_info.vactive = 0;
+                                       pdrm.u.extin_info.hactive_size = 720;
+                                       pdrm.u.extin_info.vactive_size = 576;
+                                       pdrm.u.extin_info.scantype = false;
+                                       pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_50HZ;
+                                       pdrm.u.extin_info.ntscpal_type = 0;
+                               }
+                       } else {
+                               /* get frequency status, but we don't get this status.
+                                  Suppose that frequency is not 50Hz
+                               spIError = spIAvd_GetStatus(dev,  AVD_STATUS_50HZ, &sAvdStatus);
+                               */
+                               if (b50Hz == 1) {
+                                       pdrm.u.extin_info.htotal = 1728;
+                                       pdrm.u.extin_info.vtotal = 625;
+                                       pdrm.u.extin_info.hactive = 134;
+                                       pdrm.u.extin_info.vactive = 17;
+                                       pdrm.u.extin_info.hactive_size = 720;
+                                       pdrm.u.extin_info.vactive_size = 576;
+                                       pdrm.u.extin_info.scantype = false;
+                                       pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_50HZ;
+                                       pdrm.u.extin_info.ntscpal_type = 1;
+                               } else {
+                                       pdrm.u.extin_info.htotal = 1716;
+                                       pdrm.u.extin_info.vtotal = 525;
+                                       pdrm.u.extin_info.hactive = 133;
+                                       pdrm.u.extin_info.vactive = 14;
+                                       pdrm.u.extin_info.hactive_size = 720;
+                                       pdrm.u.extin_info.vactive_size = 480;
+                                       pdrm.u.extin_info.scantype = false;
+                                       pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_60HZ;
+                                       pdrm.u.extin_info.ntscpal_type = 0;
+                               }
+                       }
+                       maru_composite_dbg(5, "[composite] g_ext_ctrls(g_signal) : htotal = %d, vtotal = %d," \
+                                       "hactive_size = %d, vactive_size = %d, scantype = %d\n",
+                                       pdrm.u.extin_info.htotal, pdrm.u.extin_info.vtotal,
+                                       pdrm.u.extin_info.hactive_size, pdrm.u.extin_info.vactive_size, pdrm.u.extin_info.scantype);
+                       pdrm.u.extin_info.syncmode = 1; // 0: HDMI or DTV, 1 otherwise
+                       pdrm.u.extin_info.pcinput = 0;
+                       pdrm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_YC;
+
+                       if (dev->source_type == AV)
+                               pdrm.inputport = V4L2_DRM_INPUTPORT_TYPE_AV;
+                       if (dev->source_type == ATV)
+                               pdrm.inputport = V4L2_DRM_INPUTPORT_TYPE_ATV;
+                       if (dev->source_type == SCART)
+                               pdrm.inputport = V4L2_DRM_INPUTPORT_TYPE_SCART;
+
+                       pdrm.reserved = 0x1234ABCD;
+                       if (copy_to_user(ctrl->string, (char *)&pdrm, sizeof(pdrm))) {
+                               maru_composite_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+               default:
+                       maru_composite_dbg(5, "unsupported ctrol id : 0x%08x\n", ctrl->id);
+                       break;
+               }
+       }
+err:
+       mutex_unlock(dev->mlock);
+       return 0;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls)
+{
+       struct maru_composite_device *dev = priv;
+       struct v4l2_ext_control *ctrl = NULL;
+       struct v4l2_vbi_lines *pvbi = NULL;
+       struct v4l2_fake_res *fake = NULL;
+       struct v4l2_pix_ext_format *pextfmt = NULL;
+       int ret = 0;
+       int i = 0;
+       int b50Hz = 0; /* FIXME: Suppose that frequency Hz is not 50 */
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_DV)
+               return ret;
+
+       mutex_lock(dev->mlock);
+       for (i = 0; i < ctrls->count; i++) {
+               ctrl = ctrls->controls + i;
+        switch (ctrl->id) {
+               case V4L2_CID_DV_S_VBILINE:
+                       pvbi = (struct v4l2_vbi_lines*)ctrl->string;
+                       if (pvbi->startline < 5 || pvbi->endline < 5) {
+                               maru_composite_err("Invalid line number(<5)\n");
+                               ret = -EINVAL;
+                               goto err;
+                       } else if (((pvbi->startline > 25) && (pvbi->startline < 272)) ||((pvbi->endline > 25) && (pvbi->endline < 272))) {
+                               maru_composite_err("Invalid line number(>5 && <272)\n");
+                               ret = -EINVAL;
+                               goto err;
+                       } else if ((pvbi->startline > 338 || pvbi->endline > 338)) {
+                               maru_composite_err("Invalid line number(>338)\n");
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       /* line standard check */
+                       if ((pvbi->linestd >= V4L2_VBI_MAN_LINE_MAX)) {
+                               maru_composite_err("Invalid enumLineStd\n");
+                               ret = -EINVAL;
+                               goto err;
+                       }
+                       /* FIXME: setVbiLineStandard */
+                       break;
+
+               case V4L2_CID_DV_S_RESOLUTION:
+                       if (dev->fake_av.flag) {
+                               if (dev->fake_av.analog_fake_res) {
+                                       dev->pix_fmt_after_lookup.hres = 720;
+                                       dev->pix_fmt_after_lookup.vres = 480;
+                                       dev->pix_fmt_after_lookup.hstart = 0;
+                                       dev->pix_fmt_after_lookup.vstart = 0;
+                                       dev->pix_fmt_after_lookup.htotal = 858;
+                                       dev->pix_fmt_after_lookup.vtotal = 525;
+                                       dev->pix_fmt_after_lookup.hfreq = 15734;
+                                       dev->pix_fmt_after_lookup.vfreq = 6000;
+                                       dev->pix_fmt_after_lookup.scantype = false;
+                               } else {
+                                       dev->pix_fmt_after_lookup.hres = 720;
+                                       dev->pix_fmt_after_lookup.vres = 576;
+                                       dev->pix_fmt_after_lookup.hstart = 0;
+                                       dev->pix_fmt_after_lookup.vstart = 0;
+                                       dev->pix_fmt_after_lookup.htotal = 864;
+                                       dev->pix_fmt_after_lookup.vtotal = 625;
+                                       dev->pix_fmt_after_lookup.hfreq = 15625;
+                                       dev->pix_fmt_after_lookup.vfreq = 5000;
+                                       dev->pix_fmt_after_lookup.scantype = false;
+                               }
+                       } else {
+                               pextfmt = (struct v4l2_pix_ext_format*)ctrl->string;
+                               switch (dev->source_type) {
+                               case AV:
+                               case ATV:
+                               case SCART:
+                                       if (b50Hz) {
+                                               dev->pix_fmt_after_lookup.hres = 720;
+                                               dev->pix_fmt_after_lookup.vres = 576;
+                                               dev->pix_fmt_after_lookup.hstart = 134;
+                                               dev->pix_fmt_after_lookup.vstart = 17;
+                                               dev->pix_fmt_after_lookup.htotal = 864;
+                                               dev->pix_fmt_after_lookup.vtotal = 625;
+                                               dev->pix_fmt_after_lookup.hfreq = 15625;
+                                               dev->pix_fmt_after_lookup.vfreq = 5000;
+                                               dev->pix_fmt_after_lookup.scantype = false;
+                                       } else {
+                                               dev->pix_fmt_after_lookup.hres = 720;
+                                               dev->pix_fmt_after_lookup.vres = 480;
+                                               dev->pix_fmt_after_lookup.hstart = 133;
+                                               dev->pix_fmt_after_lookup.vstart = 14;
+                                               dev->pix_fmt_after_lookup.htotal = 858;
+                                               dev->pix_fmt_after_lookup.vtotal = 525;
+                                               dev->pix_fmt_after_lookup.hfreq = 15734;
+                                               dev->pix_fmt_after_lookup.vfreq = 6000;
+                                               dev->pix_fmt_after_lookup.scantype = false;
+                                       }
+                               }
+                       }
+                       maru_composite_dbg(5, "[composite] hres = %d, vres = %d, hstart = %d, \
+                                       vstart = %d, htotal = %d, vtotal = %d, hfreq = %d, vfreq = %d, scantype = %d\n",
+                                       dev->pix_fmt_after_lookup.hres,
+                                       dev->pix_fmt_after_lookup.vres,
+                                       dev->pix_fmt_after_lookup.hstart,
+                                       dev->pix_fmt_after_lookup.vstart,
+                                       dev->pix_fmt_after_lookup.htotal,
+                                       dev->pix_fmt_after_lookup.vtotal,
+                                       dev->pix_fmt_after_lookup.hfreq,
+                                       dev->pix_fmt_after_lookup.vfreq,
+                                       dev->pix_fmt_after_lookup.scantype);
+                       break;
+
+               case V4L2_CID_DV_S_FAKE_RESOLUTION:
+                       fake = (struct v4l2_fake_res*)ctrl->string;
+                       dev->fake_av.flag = fake->flag;
+                       dev->fake_av.analog_fake_res = fake->analog_fake_res;
+                       break;
+               }
+       }
+err:
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+/* v4l2_ioctl_ops for COMPOSITE */
+struct v4l2_ioctl_ops maru_composite_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap,
+       .vidioc_g_ctrl = vidioc_g_ctrl,
+       .vidioc_s_ctrl = vidioc_s_ctrl,
+       .vidioc_s_input = vidioc_s_input,
+       .vidioc_streamon = vidioc_streamon,
+       .vidioc_streamoff = vidioc_streamoff,
+       .vidioc_g_std = vidioc_g_std,
+       .vidioc_s_std = vidioc_s_std,
+       .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+};
+
+const struct v4l2_ioctl_ops *get_composite_v4l2_ioctl_ops(void)
+{
+       return &maru_composite_ioctl_ops;
+}
+
+/* ------------------------------------------------------------------
+       File operations for the device
+   ------------------------------------------------------------------*/
+static int maru_composite_open(struct file *file)
+{
+       int ret = 0;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       file->private_data = dev;
+       dev->atv_lock_status = 0;
+
+       mutex_lock(dev->mlock);
+
+       iowrite32(0, dev->mmregs + MARU_COMPOSITE_OPEN);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_OPEN);
+       if (ret) {
+               maru_composite_err("Failed to open the device\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+
+       return ret;
+
+}
+static int maru_composite_close(struct file *file)
+{
+       int ret = 0;
+       struct maru_composite_device *dev = file->private_data;
+
+       maru_composite_dbg(5, "[composite]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate == S_RUNNING) {
+               maru_composite_err("The device has been terminated unexpectedly.\n");
+               iowrite32(1, dev->mmregs + MARU_COMPOSITE_STOP_PREVIEW);
+               ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_STOP_PREVIEW);
+               if (ret) {
+                       maru_composite_err("Failed to stop the preview\n");
+                       mutex_unlock(dev->mlock);
+                       return -ret;
+               }
+
+               dev->opstate = S_IDLE;
+       }
+
+       iowrite32(0, dev->mmregs + MARU_COMPOSITE_CLOSE);
+       ret = (int)ioread32(dev->mmregs + MARU_COMPOSITE_CLOSE);
+       if (ret) {
+               maru_composite_err("Failed to close the device\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+
+       return ret;
+}
+
+static unsigned int maru_composite_poll(struct file *file,
+       struct poll_table_struct *wait)
+{
+       return 0;
+}
+
+static const struct v4l2_file_operations maru_composite_fops = {
+       .owner = THIS_MODULE,
+       .open = maru_composite_open,
+       .release = maru_composite_close,
+       .poll = maru_composite_poll,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+int maru_composite_init(struct maru_extinput_device *device)
+{
+       int ret = 0;
+
+       dev = kzalloc(sizeof(struct maru_composite_device), GFP_KERNEL);
+       if (!dev) {
+               maru_composite_err("kzalloc() failed\n");
+               ret = -ENOMEM;
+               goto enomem;
+       }
+
+       dev->opstate = S_IDLE;
+       dev->mmregs = device->mmregs;
+       dev->mlock = &device->mlock;
+
+    dev->platform_type = (int)ioread32(dev->mmregs + MARU_EXTINPUT_PLATFORM_TYPE);
+    maru_composite_info("platform type: %s\n", dev->platform_type ? "hawk" : "golf");
+
+
+       vfd_composite = video_device_alloc();
+       if (!vfd_composite) {
+               maru_composite_err("failed to allocate composite video device\n");
+               ret = -ENOMEM;
+               goto video_device_alloc_failed;
+       }
+
+       vfd_composite->fops = &maru_composite_fops;
+       vfd_composite->ioctl_ops = get_composite_v4l2_ioctl_ops();
+       vfd_composite->release = video_device_release;
+       strcpy(vfd_composite->name, "maru_composite");
+       vfd_composite->vfl_dir = VFL_DIR_M2M;
+       vfd_composite->tvnorms = V4L2_SYS_TYPE_0 | V4L2_SYS_TYPE_1 | V4L2_SYS_TYPE_2 | V4L2_SYS_TYPE_3;
+       vfd_composite->dev_parent = &device->pdev->dev;
+       vfd_composite->v4l2_dev = &device->v4l2_dev;
+
+       ret = video_register_device(vfd_composite, VFL_TYPE_GRABBER, COMPOSITE_DEVICE);
+       if (ret) {
+               maru_composite_err("failed to register composite video device\n");
+               goto video_register_device_failed;
+       }
+
+       return ret;
+
+video_register_device_failed:
+       video_device_release(vfd_composite);
+       vfd_composite = NULL;
+video_device_alloc_failed:
+       kfree(dev);
+enomem:
+       return ret;
+}
+
+void maru_composite_release(void)
+{
+       if (vfd_composite) {
+               video_unregister_device(vfd_composite);
+               video_device_release(vfd_composite);
+       }
+
+       if (dev)
+               kfree(dev);
+
+       vfd_composite = NULL;
+       dev = NULL;
+}
diff --git a/drivers/maru/tv/maru_external_input.h b/drivers/maru/tv/maru_external_input.h
new file mode 100644 (file)
index 0000000..710aba3
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * MARU Virtual External Input Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * GunSoo Kim <gunsoo83.kim@samsung.com>
+ * HyunJin Lee <hyunjin816.lee@samsung.com>
+ * SangHo Park <sangho1206.park@samsung.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/videodev2.h>
+
+#ifndef _MARU_EXTERNAL_INPUT_H_
+#define _MARU_EXTERNAL_INPUT_H_
+
+#define MARU_EXTINPUT_INIT           0x00
+#define MARU_EXTINPUT_ISR                       0x04
+#define MARU_EXTINPUT_ISR_STATE      0x08
+#define MARU_EXTINPUT_S_DATA         0x0C
+#define MARU_EXTINPUT_G_DATA         0x10
+#define MARU_EXTINPUT_DTC            0x14
+#define MARU_EXTINPUT_ONOFF_STATE    0x18
+
+#define MARU_HDMI_OPEN               0x1C
+#define MARU_HDMI_CLOSE              0x20
+#define MARU_HDMI_START_PREVIEW      0x24
+#define MARU_HDMI_STOP_PREVIEW       0x28
+#define MARU_HDMI_S_FMT              0x2C
+#define MARU_HDMI_G_FMT              0x30
+#define MARU_HDMI_S_CTRL             0x34
+#define MARU_HDMI_S_EXT_CTRLS        0x38
+#define MARU_HDMI_G_EXT_CTRLS        0x3C
+
+#define MARU_COMPOSITE_OPEN          0x40
+#define MARU_COMPOSITE_CLOSE         0x44
+#define MARU_COMPOSITE_START_PREVIEW 0x48
+#define MARU_COMPOSITE_STOP_PREVIEW  0x4C
+#define MARU_COMPOSITE_S_INPUT       0x50
+#define MARU_COMPOSITE_S_CTRL        0x54
+#define MARU_COMPOSITE_G_EXT_CTRLS   0x58
+
+#define MARU_COMPONENT_OPEN          0x5C
+#define MARU_COMPONENT_CLOSE         0x60
+#define MARU_COMPONENT_START_PREVIEW 0x64
+#define MARU_COMPONENT_STOP_PREVIEW  0x68
+#define MARU_COMPONENT_S_CTRL        0x6C
+#define MARU_COMPONENT_S_EXT_CTRLS   0x70
+#define MARU_COMPONENT_G_EXT_CTRLS   0x74
+
+#define MARU_SWITCH_S_INPUT          0x78
+#define MARU_ATV_LOCK_STATUS         0x7C
+#define MARU_EXTINPUT_PLATFORM_TYPE  0x80
+
+#define DFL_WIDTH      1920
+#define DFL_HEIGHT     1080
+
+#define HDMI_SWITCH_DEVICE           28
+#define HDMI_PROCESSOR_DEVICE        21
+
+#define STATE_CHECKER                0xffff
+#define STATE_SHIFT                  16
+#define MAX_EXTINPUT_COUNT           9
+
+#define HDMI_DEV_COUNT               4
+#define CLASS_NAME                   "gpio-tv-feature"
+
+/* add new v4l2 structure, define to be compatible with target */
+#define V4L2_CTRL_CLASS_DV                  0x00a00000 /* Digital Video controls */
+
+/*  DV-class control IDs defined by V4L2 */
+#define V4L2_CID_DV_CLASS_BASE              (V4L2_CTRL_CLASS_DV | 0x900)
+#define V4L2_CID_DV_CLASS                   (V4L2_CTRL_CLASS_DV | 1)
+#define V4L2_CID_DV_TX_HOTPLUG              (V4L2_CID_DV_CLASS_BASE + 1)
+#define V4L2_CID_DV_TX_RXSENSE              (V4L2_CID_DV_CLASS_BASE + 2)
+#define V4L2_CID_DV_TX_EDID_PRESENT         (V4L2_CID_DV_CLASS_BASE + 3)
+#define V4L2_CID_DV_TX_MODE                 (V4L2_CID_DV_CLASS_BASE + 4)
+#define V4L2_CID_DV_TX_RGB_RANGE            (V4L2_CID_DV_CLASS_BASE + 5)
+#define V4L2_CID_DV_RX_POWER_PRESENT        (V4L2_CID_DV_CLASS_BASE + 100)
+#define V4L2_CID_DV_RX_RGB_RANGE            (V4L2_CID_DV_CLASS_BASE + 101)
+
+/* added for samsung DTV*/
+#define V4L2_CID_DV_G_RESOLUTION        (V4L2_CID_DV_CLASS_BASE + 102)
+#define V4L2_CID_DV_G_HDMI_STATUS       (V4L2_CID_DV_CLASS_BASE + 103)
+#define V4L2_CID_DV_G_SIGNAL_INFO       (V4L2_CID_DV_CLASS_BASE + 104)
+#define V4L2_CID_DV_G_BLACKLEVEL            (V4L2_CID_DV_CLASS_BASE + 105)
+#define V4L2_CID_DV_S_RESOLUTION        (V4L2_CID_DV_CLASS_BASE + 106)
+#define V4L2_CID_DV_S_BLACKLEVEL        (V4L2_CID_DV_CLASS_BASE + 107)
+#define V4L2_CID_DV_S_PC_MODE           (V4L2_CID_DV_CLASS_BASE + 108)
+#define V4L2_CID_DV_S_VBILINE           (V4L2_CID_DV_CLASS_BASE + 109)
+#define V4L2_CID_DV_S_VBI_ONOFF         (V4L2_CID_DV_CLASS_BASE + 110)
+#define V4L2_CID_DV_G_WSS_INFO          (V4L2_CID_DV_CLASS_BASE + 111)
+#define V4L2_CID_DV_G_VPS_INFO          (V4L2_CID_DV_CLASS_BASE + 112)
+
+#define V4L2_CID_DV_S_ARC               (V4L2_CID_DV_CLASS_BASE + 102 +50)
+#define V4L2_CID_DV_S_HOTPLUG           (V4L2_CID_DV_CLASS_BASE + 103 +50)
+#define V4L2_CID_DV_MHL_SENSE           (V4L2_CID_DV_CLASS_BASE + 104 +50)
+#define V4L2_CID_DV_G_ARC_PORT          (V4L2_CID_DV_CLASS_BASE + 105 +50)
+#define V4L2_CID_DV_G_MHL_PORT          (V4L2_CID_DV_CLASS_BASE + 106 +50)
+#define V4L2_CID_DV_S_CLK_TERM          (V4L2_CID_DV_CLASS_BASE + 107 +50)
+#define V4L2_CID_DV_G_CLK_TERM_DURATION (V4L2_CID_DV_CLASS_BASE + 108 +50)
+#define V4L2_CID_DV_G_SWITCH_INFO       (V4L2_CID_DV_CLASS_BASE + 109 +50)
+#define V4L2_CID_DV_S_CONDITION         (V4L2_CID_DV_CLASS_BASE + 110 +50)
+#define V4L2_CID_DV_S_PATTERN           (V4L2_CID_DV_CLASS_BASE + 111 +50)
+#define V4L2_CID_DV_S_FACTORY_ITEM      (V4L2_CID_DV_CLASS_BASE + 112 +50)
+#define V4L2_CID_DV_S_DEPENDENCE        (V4L2_CID_DV_CLASS_BASE + 113 +50)
+#define V4L2_CID_DV_S_FAKE_RESOLUTION   (V4L2_CID_DV_CLASS_BASE + 114 +50)
+#define V4L2_CID_DV_T_LOOPBACK          (V4L2_CID_DV_CLASS_BASE + 115 +50)
+#define V4L2_CID_DV_CONVERT_VAL         (V4L2_CID_DV_CLASS_BASE + 116 +50)
+#define V4L2_CID_DV_G_HDMI_INFO         (V4L2_CID_DV_CLASS_BASE + 121 +50)
+#define V4L2_CID_DV_G_EDID              (V4L2_CID_DV_CLASS_BASE + 124 +50)
+#define V4L2_CID_DV_SEND_CMD            (V4L2_CID_DV_CLASS_BASE + 125 +50)
+#define V4L2_CID_DV_G_BUS_DATA          (V4L2_CID_DV_CLASS_BASE + 126 +50)
+
+#define V4L2_CID_DV_S_FAKE_RESOLUTION       (V4L2_CID_DV_CLASS_BASE + 114 +50)
+#define V4L2_CID_DV_G_HDMI_INFO         (V4L2_CID_DV_CLASS_BASE + 121 +50)
+
+/* for video ouput */
+#define V4L2_CID_DV_VIDEO_OUTPUT            (V4L2_CID_DV_CLASS_BASE + 200)
+
+struct v4l2_pix_ext_format {
+       __u32 hres;
+       __u32 vres;
+       __u32 hstart;
+       __u32 vstart;
+       __u32 htotal;
+       __u32 vtotal;
+       __u32 hfreq;
+       __u32 vfreq;
+       __u32 scantype;
+       __u32 hsyncpositive;
+       __u32 vsyncpositive;
+       __u32 nearfreqency;
+       __u32 inputFormat;
+       __u32 outputFormat;
+       __u32 reserved[8];
+};
+
+struct v4l2_dv_hdmi_status {
+       __u32 inputsignaltype;  ///< 0: DVI  1: HDMI
+       __u32 inputFormat;              ///< in videodev2.h,
+                                                       /// RGB - V4L2_PIX_FMT_RGB444,
+                                                       /// YUV422 : V4L2_PIX_FMT_YUV422P,
+                                                       /// YUV444 : V4L2_PIX_FMT_YUV444
+       __u32 videocolordepth;  ///< HDMI Color Depth.
+                                                       //in videodev2.h, v4l2_colordepth
+       __u32 mutestatus;               ///< true : mute, false : unmute
+       __u32 acplevel;                 ///< HDMI ACP Level
+};
+
+typedef enum {
+       HDMI_SIGNAL_INTERLACED = 0,
+       HDMI_SIGNAL_PROGRESSIVE
+} HDMI_ScanType;
+
+typedef enum {
+       V4L2_DV_INFOPACKET_VS     =1,    ///< Vendor Specific InfoFrame
+       V4L2_DV_INFOPACKET_AVI    =2,   ///< Auxiliary Video Information InfoFrame
+       V4L2_DV_INFOPACKET_SPD    =3,   ///< Source Product Description InfoFrame
+       V4L2_DV_INFOPACKET_AUDIO  =4, ///< Audio InfoFrame
+       V4L2_DV_INFOPACKET_MPEG   =5,  ///< MPEG Source InfoFrame
+       V4L2_DV_INFOPACKET_GAMUT  =6, ///< Gamut Metadata Packet
+       V4L2_DV_INFOPACKET_ACP    =7,   ///< Audio Content Protection packet
+       V4L2_DV_INFOPACKET_UNREC  =8, ///< Unrecognized packet
+}v4l2_dv_infopacket_type ; ///< HDMI Info Frame Type and Packet Type
+
+typedef enum {
+       V4L2_COLORDEPTH_24BIT = 1,
+       V4L2_COLORDEPTH_30BIT = 2,
+       V4L2_COLORDEPTH_36BIT = 3,
+       V4L2_COLORDEPTH_48BIT = 4
+}v4l2_dv_colordepth_type;
+
+enum {
+       _ISR_FILLBUF = 1,
+       _ISR_EXTINPUT
+};
+
+enum input_type {
+       AV,
+       ATV,
+       COMPONENT,
+       SCART,
+       HDMI
+};
+
+typedef enum {
+       AVD_STATUS_SYNC_LOCK            = 0x0001,   ///< Lock Check
+       AVD_STATUS_MACRO                = 0x0002,   ///< Macrovision
+       AVD_STATUS_CGMS             = 0x0004,   ///< Copy Generation Management System
+       AVD_STATUS_NOISE                = 0x0008,
+       //AVD_080611_1
+       AVD_STATUS_ALLOW_3DCOMB = 0x0010,///< CVBS sync tip Noise
+       AVD_STATUS_50HZ             = 0x0020,///< CVBS sync tip Noise
+       AVD_STATUS_STANDARD         = 0x0040, // Detect input standard
+       AVD_STATUS_SCART            = 0x0080, // Detect input standard
+       AVD_STATUS_BURST            = 0x0100, // Detect BURST
+       AVD_STATUS_SCAN_LOCK            = 0x0200, // Auto Program Lock
+       AVD_STATUS_PAL_SW           = 0x0400 // PAL SWing
+} Avd_StatusMask_e;
+
+typedef enum {
+       INFO_FRAME_AVI = 0,
+       INFO_FRAME_AUD,
+       INFO_FRAME_SPD,
+       INFO_FRAME_ACP,
+       INFO_FRAME_MPEG,
+       INFO_FRAME_VSI,
+       INFO_FRAME_UNREC,
+       INFO_FRAME_UNKNOWN
+}HdmiInfoPackets_e;
+
+struct maru_extinput_device {
+       struct v4l2_device              v4l2_dev;
+       struct mutex                    mlock;
+       struct video_device             *vfd_hdmi_switch;
+       struct pci_dev                  *pdev;
+
+       int switch_input;
+
+       void __iomem                    *mmregs;
+       resource_size_t                 io_base;
+       resource_size_t                 io_size;
+       resource_size_t                 mem_base;
+       resource_size_t                 mem_size;
+};
+
+int maru_hdmi_init(struct maru_extinput_device *device);
+void maru_hdmi_release(void);
+int maru_composite_init(struct maru_extinput_device *device);
+void maru_composite_release(void);
+int maru_component_init(struct maru_extinput_device *device);
+void maru_component_release(void);
+
+int get_hdmi_onoff_status(int index);
+int get_composite_status(void);
+int get_component_status(void);
+#endif //_MARU_EXTERNAL_INPUT_H_
diff --git a/drivers/maru/tv/maru_hdmi.c b/drivers/maru/tv/maru_hdmi.c
new file mode 100644 (file)
index 0000000..970c81d
--- /dev/null
@@ -0,0 +1,772 @@
+/*
+ * MARU Virtual HDMI Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * GunSoo Kim <gunsoo83.kim@samsung.com>
+ * Hyunjin Lee <hyunjin816.lee@samsung.com>
+ * SangHo Park <sangho1206.park@samsung.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "maru_v4l2.h"
+#include "maru_external_input.h"
+
+static int extinput_hdmi_debug = 0;
+module_param(extinput_hdmi_debug, int, 0644);
+MODULE_PARM_DESC(extinput_hdmi_debug, "Turn on/off maru external input hdmi debugging (default:off).");
+
+#define HDMI_CONNECTED      0
+#define HDMI_DISCONNECTED   1
+#define HDMI_LOCKED         1
+#define HDMI_UNLOCKED       0
+
+#define maru_hdmi_err(fmt, arg...) \
+       printk(KERN_ERR "[ERR]maru_hdmi[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmi_warn(fmt, arg...) \
+       printk(KERN_WARNING "[WARN]maru_hdmi[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmi_info(fmt, arg...) \
+       printk(KERN_INFO "[INFO]maru_hdmi[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmi_dbg(level, fmt, arg...) \
+       do { \
+               if (extinput_hdmi_debug >= (level)) { \
+                       printk(KERN_INFO "[DBG]maru_hdmi[%s]: " fmt, \
+                               __func__, ##arg); \
+               } \
+       } while (0)
+
+enum maru_hdmi_opstate {
+       S_IDLE = 0,
+       S_RUNNING = 1
+};
+
+struct maru_hdmi_device {
+       struct mutex            *mlock;
+       void __iomem            *mmregs;
+       enum maru_hdmi_opstate  opstate;
+
+       struct v4l2_pix_ext_format pix_fmt_after_lookup;
+       int platform_type;
+       int                     bytesperline;
+       int                     sizeimage;
+       int                     hdmi_input;
+       unsigned int            video_refs;
+       unsigned int            is_fake_resolution;
+       unsigned int            is_pc_mode;
+       unsigned int            is_blacklevel;
+};
+
+struct maru_extinput_pixfmt {
+       __u32 fmt;
+       __u8 depth;
+};
+
+static struct maru_hdmi_device *dev = NULL;
+static struct video_device *vfd_hdmi = NULL;
+
+#define MARU_HDMI_MODULE_NAME "maru_hdmi"
+#define HDMI_DEVICE 21
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void  *priv,
+                                                       struct v4l2_capability *cap)
+{
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       strcpy(cap->driver, MARU_HDMI_MODULE_NAME);
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+       return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+                                                               struct v4l2_format *f)
+{
+       int ret = 0;
+       int array_size = 0;
+       int index = 0;
+       int depth = 0;
+       struct maru_hdmi_device *dev = priv;
+       struct maru_extinput_pixfmt supported_dst_pixfmts[] = {
+               { V4L2_PIX_FMT_YUYV, 16 },
+               { V4L2_PIX_FMT_YUV420, 12 },
+               { V4L2_PIX_FMT_YVU420, 12 },
+               { V4L2_PIX_FMT_YUV444, 16 },
+       };
+
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       if (f->fmt.pix.pixelformat == dev->pix_fmt_after_lookup.outputFormat) {
+               maru_hdmi_dbg(5, "set this format(0x%x) already\n",
+                                               dev->pix_fmt_after_lookup.outputFormat);
+               goto err;
+       }
+
+       /* check pixel format */
+       array_size = ARRAY_SIZE(supported_dst_pixfmts);
+       for (index = 0; index < array_size; index++) {
+               if (supported_dst_pixfmts[index].fmt == f->fmt.pix.pixelformat) {
+                       depth = supported_dst_pixfmts[index].depth;
+                       break;
+               }
+       }
+       if (index == array_size) {
+               maru_hdmi_dbg(5, "Not supported format: 0x%x\n", f->fmt.pix.pixelformat);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_lock(dev->mlock);
+
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(f->fmt.pix.pixelformat, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_HDMI_S_FMT);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_S_FMT);
+       if (ret) {
+               maru_hdmi_err("Failed to set the format: %d\n", -ret);
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->pix_fmt_after_lookup.outputFormat = f->fmt.pix.pixelformat;
+       dev->bytesperline   = dev->pix_fmt_after_lookup.hres * depth;
+       dev->sizeimage      = dev->pix_fmt_after_lookup.vres * dev->bytesperline;
+
+       mutex_unlock(dev->mlock);
+
+err:
+       return ret;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       struct maru_hdmi_device *dev = priv;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       dev->hdmi_input = i;
+
+       return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                                                       struct v4l2_control *ctrl)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = priv;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(ctrl->id, dev->mmregs + MARU_EXTINPUT_S_DATA);
+       iowrite32(ctrl->value, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_HDMI_S_CTRL);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_S_CTRL);
+       if (ret) {
+               maru_hdmi_err("Failed to set the control value\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = priv;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_IDLE) {
+               maru_hdmi_err("The device state is not S_IDLE\n");
+               mutex_unlock(dev->mlock);
+               return -EBUSY;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_HDMI_START_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_START_PREVIEW);
+       if (ret) {
+               maru_hdmi_err("Failed to start preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->is_fake_resolution = 0;
+       dev->opstate = S_RUNNING;
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = priv;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+       if (dev->opstate != S_RUNNING) {
+               maru_hdmi_err("The device state is not S_RUNNING. Do nothing\n");
+               mutex_unlock(dev->mlock);
+               return ret;
+       }
+
+       iowrite32(1, dev->mmregs + MARU_HDMI_STOP_PREVIEW);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_STOP_PREVIEW);
+       if (ret) {
+               maru_hdmi_err("Failed to stop preview\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->opstate = S_IDLE;
+       if (ret) {
+               maru_hdmi_err("Failed to stream off the video buffer: %d\n", ret);
+       }
+
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+                                                               struct v4l2_input *inp)
+{
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       /* TODO: always true. check again later */
+       inp->status = true;
+
+       return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                                                               struct v4l2_ext_controls *ctrls)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = priv;
+       struct v4l2_ext_control *ctrl = NULL;
+       struct v4l2_pix_ext_format extfmt;
+       struct v4l2_dv_hdmi_status hdmi_status;
+       struct v4l2_drm pdrm;
+       unsigned char *generic;
+       unsigned int uPacketSize = 0;
+       HdmiInfoPackets_e eHdmiInfoPacket = INFO_FRAME_UNKNOWN;
+       int i = 0;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+       for (i = 0; i < ctrls->count; i++) {
+               ctrl = ctrls->controls + i;
+               //switch (maru_convert_ctrl(dev->platform_type, ctrl->id)) {
+               switch (ctrl->id) {
+               case V4L2_CID_DV_G_RESOLUTION:
+                       if (dev->is_fake_resolution) {
+                               extfmt.hres = 1280;
+                               extfmt.vres = 720;
+                               extfmt.hstart = 0;
+                               extfmt.vstart = 0;
+                               extfmt.htotal = 1650;
+                               extfmt.vtotal = 750;
+                               extfmt.vfreq = 6000;
+                               extfmt.scantype = HDMI_SIGNAL_PROGRESSIVE;
+                               extfmt.hsyncpositive = 0;
+                               extfmt.vsyncpositive = 0;
+                       } else {
+                               iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+
+                               iowrite32(0, dev->mmregs + MARU_HDMI_G_EXT_CTRLS);
+                               ret = (int)ioread32(dev->mmregs + MARU_HDMI_G_EXT_CTRLS);
+                               if (ret) {
+                                       maru_hdmi_err("Failed to get the resolution: %d\n", -ret);
+                                       ret = -ret;
+                                       goto err;
+                               }
+
+                               /* FIXME : Suppose that TV resolution is 1080I */
+                               //extfmt = (struct v4l2_pix_ext_format *)ctrl->string;
+                               extfmt.hsyncpositive = 1;
+                               extfmt.vsyncpositive = 1;
+                               extfmt.hres = ioread32(dev->mmregs + MARU_EXTINPUT_G_DATA);
+                               extfmt.vres = ioread32(dev->mmregs + MARU_EXTINPUT_G_DATA);
+                               extfmt.hstart = 0;
+                               extfmt.vstart = 0;
+                               extfmt.htotal = 2200;
+                               extfmt.vtotal = 562;
+                               extfmt.hfreq = 33750;
+                               extfmt.vfreq = 6000;
+                               extfmt.scantype = 1;
+                       }
+                       if (copy_to_user(ctrl->string, (char *)&extfmt, sizeof(extfmt))) {
+                               maru_hdmi_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+
+               case V4L2_CID_DV_G_HDMI_STATUS:
+                       /* FIXME : need modification */
+                       hdmi_status.inputsignaltype = 1;
+                       hdmi_status.videocolordepth = V4L2_COLORDEPTH_24BIT;
+                       hdmi_status.inputFormat = V4L2_DRM_COLORFORMAT_YUV444;
+                       dev->pix_fmt_after_lookup.inputFormat = hdmi_status.inputFormat;
+
+                       if (copy_to_user(ctrl->string, (char *)&hdmi_status, sizeof(hdmi_status))) {
+                               maru_hdmi_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+
+               case V4L2_CID_DV_G_SIGNAL_INFO:
+                       pdrm.inputport = V4L2_DRM_INPUTPORT_TYPE_HDMI;
+                       pdrm.reserved = 0x1234ABCD;
+                       if ((dev->pix_fmt_after_lookup.hres == 1280 ) &&
+                                       ((dev->pix_fmt_after_lookup.vres == 1024) ||
+                                       (dev->pix_fmt_after_lookup.vres == 720))) {
+                               dev->pix_fmt_after_lookup.scantype = HDMI_SIGNAL_PROGRESSIVE;
+                       }
+
+                       pdrm.u.extin_info.htotal = dev->pix_fmt_after_lookup.htotal;
+                       pdrm.u.extin_info.vtotal = dev->pix_fmt_after_lookup.vtotal;
+                       pdrm.u.extin_info.hactive = dev->pix_fmt_after_lookup.hstart;
+                       pdrm.u.extin_info.vactive = dev->pix_fmt_after_lookup.vstart;
+                       pdrm.u.extin_info.hactive_size = dev->pix_fmt_after_lookup.hres;
+                       pdrm.u.extin_info.vactive_size = dev->pix_fmt_after_lookup.vres;
+                       pdrm.u.extin_info.scantype = dev->pix_fmt_after_lookup.scantype;
+
+                       if(pdrm.u.extin_info.hactive_size > 1920)
+                               pdrm.u.extin_info.oversampling = 1;
+                       else
+                               pdrm.u.extin_info.oversampling = 0;
+
+                       if (dev->pix_fmt_after_lookup.vfreq <= 2398)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_23_97HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 2450)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_24HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 2749)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_25HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 2998)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_29_97HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 4000)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_30HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 4796)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE__47_94HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 4820)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_48HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 5497)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_50HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 5997)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_59_94HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 6400)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_60HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 8000)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_75HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 11000)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_100HZ;
+                       else if (dev->pix_fmt_after_lookup.vfreq <= 12100)
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_120HZ;
+                       else
+                               pdrm.u.extin_info.framerate = V4L2_DRM_FRAMERATE_60HZ;
+
+                       switch (dev->pix_fmt_after_lookup.outputFormat) {
+                       case V4L2_PIX_FMT_YUV444:
+                               pdrm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_YUV444;
+                               break;
+                       case V4L2_PIX_FMT_YUV422P:
+                               pdrm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_YUV422;
+                               break;
+                               case V4L2_PIX_FMT_RGB444:
+                               pdrm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_RGB444;
+                               break;
+                       default:
+                               pdrm.u.extin_info.colorformat = V4L2_DRM_COLORFORMAT_YUV444;
+                       }
+
+                       pdrm.u.extin_info.syncmode = 0;
+                       pdrm.u.extin_info.pcinput = dev->is_pc_mode;
+                       pdrm.u.extin_info.ntscpal_type = 0;
+
+                       if (copy_to_user(ctrl->string, (char *)&pdrm, sizeof(pdrm))) {
+                               maru_hdmi_err("copy_to_user failed\n");
+                               ret = -EFAULT;
+                               goto err;
+                       }
+                       break;
+
+               case V4L2_CID_DV_G_HDMI_INFO:
+                       generic = (unsigned char *)ctrl->string;
+                       switch (generic[0]) {
+                       case V4L2_DV_INFOPACKET_AVI:
+                               uPacketSize = 13;
+                               eHdmiInfoPacket = INFO_FRAME_AVI;
+                               break;
+                       case V4L2_DV_INFOPACKET_SPD:
+                               uPacketSize = 25;
+                               eHdmiInfoPacket = INFO_FRAME_SPD;
+                               break;
+                       case V4L2_DV_INFOPACKET_AUDIO:
+                               uPacketSize = 10;
+                               eHdmiInfoPacket = INFO_FRAME_AUD;
+                               break;
+                       case V4L2_DV_INFOPACKET_MPEG:
+                               uPacketSize = 10;
+                               eHdmiInfoPacket = INFO_FRAME_MPEG;
+                               break;
+                       case V4L2_DV_INFOPACKET_ACP:
+                               uPacketSize = 30;
+                               eHdmiInfoPacket = INFO_FRAME_ACP;
+                               break;
+                       case V4L2_DV_INFOPACKET_UNREC:
+                               uPacketSize = 31;
+                               eHdmiInfoPacket = INFO_FRAME_UNREC;
+                               break;
+                       case V4L2_DV_INFOPACKET_VS:
+                               uPacketSize = 6;
+                               eHdmiInfoPacket = INFO_FRAME_VSI;
+                               break;
+                       case V4L2_DV_INFOPACKET_GAMUT:
+                               maru_hdmi_err("Info Pkt GAMUT not supported\n");
+                               goto err;
+                       default:
+                               maru_hdmi_err("Invalid Info type(%d)\n", generic[0]);
+                               goto err;
+                       }
+
+                       if (uPacketSize == 0) {
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       /* FIXME: need more information */
+                       break;
+
+               case V4L2_CID_DV_G_BLACKLEVEL:
+                       ctrl->value = dev->is_blacklevel;
+                       break;
+
+               default:
+                       maru_hdmi_err("Invalid Control Id.\n");
+                       ret = -ENOENT;
+                       goto err;
+               }
+       }
+err:
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+                                                               struct v4l2_ext_controls *ctrls)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = priv;
+       struct v4l2_fake_res *fake = NULL;
+       struct v4l2_ext_control *ctrl;
+       struct v4l2_pix_ext_format extfmt;
+       int i = 0;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+
+       for (i = 0; i < ctrls->count; i++) {
+               ctrl = ctrls->controls + i;
+               //switch (maru_convert_ctrl(dev->platform_type, ctrl->id)) {
+               switch (ctrl->id) {
+                       case V4L2_CID_DV_S_FAKE_RESOLUTION:
+                               fake = (struct v4l2_fake_res*)ctrl->string;
+                               dev->is_fake_resolution = fake->flag;
+                               break;
+
+                       case V4L2_CID_DV_S_RESOLUTION:
+                               if (copy_from_user((char *)&extfmt, ctrl->string, sizeof(extfmt))) {
+                                       maru_hdmi_err("copy_to_user failed\n");
+                                       ret = -EFAULT;
+                                       goto err;
+                               }
+
+                               if (dev->is_fake_resolution) {
+                                       dev->pix_fmt_after_lookup.hres = 1280;
+                                       dev->pix_fmt_after_lookup.vres = 720;
+                                       dev->pix_fmt_after_lookup.hstart = 0;
+                                       dev->pix_fmt_after_lookup.vstart = 0;
+                                       dev->pix_fmt_after_lookup.htotal = 1650;
+                                       dev->pix_fmt_after_lookup.vtotal = 750;
+                                       dev->pix_fmt_after_lookup.vfreq = 6000;
+                                       dev->pix_fmt_after_lookup.scantype = HDMI_SIGNAL_PROGRESSIVE;
+                                       dev->pix_fmt_after_lookup.hsyncpositive = 0;
+                                       dev->pix_fmt_after_lookup.vsyncpositive = 0;
+                               } else {
+                                       if((extfmt.hres) && (extfmt.vres)) {
+#if 0
+                                               iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+                                               iowrite32(pextfmt->hres, dev->mmregs + MARU_EXTINPUT_S_DATA);
+                                               iowrite32(pextfmt->vres, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+                                               iowrite32(0, dev->mmregs + MARU_HDMI_S_EXT_CTRLS);
+                                               ret = (int)ioread32(dev->mmregs + MARU_HDMI_S_EXT_CTRLS);
+                                               if (ret) {
+                                                       maru_hdmi_err("Failed to set the resolution: %d\n", -ret);
+                                                       ret = -ret;
+                                                       goto err;
+                                               }
+#endif
+                                               dev->pix_fmt_after_lookup.hres = extfmt.hres;
+                                               dev->pix_fmt_after_lookup.vres = extfmt.vres;
+                                               dev->pix_fmt_after_lookup.hstart = extfmt.hstart;
+                                               dev->pix_fmt_after_lookup.vstart = extfmt.vstart;
+                                               dev->pix_fmt_after_lookup.htotal = extfmt.htotal;
+                                               dev->pix_fmt_after_lookup.vtotal = extfmt.vtotal;
+                                               dev->pix_fmt_after_lookup.hfreq = extfmt.hfreq;
+                                               dev->pix_fmt_after_lookup.vfreq = extfmt.vfreq;
+                                       } else {
+                                               maru_hdmi_err("Resolution not set, First Call VIDIOC_G_EXT_CTRLS with control.id = V4L2_CID_DV_RESOLUTION_INFO\n");
+                                               ret = -EINVAL;
+                                               goto err;
+                                       }
+                               }
+                               break;
+
+                       case V4L2_CID_DV_S_BLACKLEVEL:
+                               if (ctrl->value <= 1) {
+                                       dev->is_blacklevel = ctrl->value;
+                               } else {
+                                       maru_hdmi_err("Invalid input value\n");
+                               }
+                               break;
+
+                       case V4L2_CID_DV_S_PC_MODE:
+                               if (ctrl->value == 0 || ctrl->value == 1) {
+                                       dev->is_pc_mode = ctrl->value;
+                               } else {
+                                       maru_hdmi_err("Invalid input value for is_pc_mode\n");
+                               }
+
+                       default:
+                               break;
+               }
+       }
+
+err:
+       mutex_unlock(dev->mlock);
+       return ret;
+}
+
+/* v4l2_ioctl_ops for HDMI */
+struct v4l2_ioctl_ops maru_hdmi_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap,
+       .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+       .vidioc_s_input = vidioc_s_input,
+       .vidioc_s_ctrl = vidioc_s_ctrl, /* for video output settings */
+       .vidioc_streamon = vidioc_streamon,
+       .vidioc_streamoff = vidioc_streamoff,
+       .vidioc_enum_input = vidioc_enum_input,
+       .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls
+};
+
+const struct v4l2_ioctl_ops *get_hdmi_v4l2_ioctl_ops(void)
+{
+       return &maru_hdmi_ioctl_ops;
+}
+
+/* ------------------------------------------------------------------
+       File operations for the device
+       ------------------------------------------------------------------*/
+static int maru_hdmi_open(struct file *file)
+{
+       int ret = 0;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       file->private_data = dev;
+
+       mutex_lock(dev->mlock);
+       if (dev->video_refs) {
+               dev->video_refs++;
+               maru_hdmi_dbg(5, "[hdmi] video_refs = %d\n", dev->video_refs);
+               mutex_unlock(dev->mlock);
+               return ret;
+       }
+       dev->pix_fmt_after_lookup.hres = DFL_WIDTH;
+       dev->pix_fmt_after_lookup.vres = DFL_HEIGHT;
+       dev->pix_fmt_after_lookup.outputFormat = V4L2_PIX_FMT_YUYV;
+
+       iowrite32(0, dev->mmregs + MARU_HDMI_OPEN);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_OPEN);
+       if (ret) {
+               maru_hdmi_err("Failed to open the device\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       dev->video_refs = 1;
+       mutex_unlock(dev->mlock);
+
+       maru_hdmi_dbg(5, "[hdmi] video_refs = %d\n", dev->video_refs);
+
+       return ret;
+}
+
+static int maru_hdmi_close(struct file *file)
+{
+       int ret = 0;
+       struct maru_hdmi_device *dev = file->private_data;
+
+       maru_hdmi_dbg(5, "[hdmi]enter\n");
+
+       mutex_lock(dev->mlock);
+       dev->video_refs--;
+       if (dev->video_refs > 0) {
+               maru_hdmi_dbg(5, "[hdmi] video_refs = %d\n", dev->video_refs);
+               mutex_unlock(dev->mlock);
+               return ret;
+       }
+
+       if (dev->opstate == S_RUNNING) {
+               maru_hdmi_err("The device has been terminated unexpectedly.\n");
+               iowrite32(1, dev->mmregs + MARU_HDMI_STOP_PREVIEW);
+               ret = (int)ioread32(dev->mmregs + MARU_HDMI_STOP_PREVIEW);
+               if (ret) {
+                       maru_hdmi_err("Failed to stop the preview\n");
+                       mutex_unlock(dev->mlock);
+                       return -ret;
+               }
+
+               dev->opstate = S_IDLE;
+       }
+
+       iowrite32(0, dev->mmregs + MARU_HDMI_CLOSE);
+       ret = (int)ioread32(dev->mmregs + MARU_HDMI_CLOSE);
+       if (ret) {
+               maru_hdmi_err("Failed to close the device\n");
+               mutex_unlock(dev->mlock);
+               return -ret;
+       }
+
+       mutex_unlock(dev->mlock);
+
+       return ret;
+}
+
+static const struct v4l2_file_operations maru_hdmi_fops = {
+       .owner = THIS_MODULE,
+       .open = maru_hdmi_open,
+       .release = maru_hdmi_close,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+int maru_hdmi_init(struct maru_extinput_device *device)
+{
+       int ret = 0;
+
+       dev = kzalloc(sizeof(struct maru_hdmi_device), GFP_KERNEL);
+       if (!dev) {
+               maru_hdmi_err("kzalloc() failed\n");
+               ret = -ENOMEM;
+               goto enomem;
+       }
+
+       dev->opstate = S_IDLE;
+       dev->mmregs = device->mmregs;
+       dev->mlock = &device->mlock;
+
+       dev->platform_type = (int)ioread32(dev->mmregs + MARU_EXTINPUT_PLATFORM_TYPE);
+       maru_hdmi_info("platform type: %s\n", dev->platform_type ? "hawk" : "golf");
+
+
+       vfd_hdmi = video_device_alloc();
+       if (!vfd_hdmi) {
+               maru_hdmi_err("failed to allocate hdmi video device\n");
+               ret = -ENOMEM;
+               goto video_device_alloc_failed;
+       }
+
+       vfd_hdmi->fops = &maru_hdmi_fops;
+       vfd_hdmi->ioctl_ops = get_hdmi_v4l2_ioctl_ops();
+       vfd_hdmi->release = video_device_release;
+       strcpy(vfd_hdmi->name, "maru_hdmi");
+       vfd_hdmi->dev_parent = &device->pdev->dev;
+       vfd_hdmi->v4l2_dev = &device->v4l2_dev;
+
+       ret = video_register_device(vfd_hdmi, VFL_TYPE_GRABBER, HDMI_DEVICE);
+       if (ret) {
+               maru_hdmi_err("failed to register hdmi video device\n");
+               goto video_register_device_failed;
+       }
+
+       return ret;
+
+video_register_device_failed:
+       video_device_release(vfd_hdmi);
+       vfd_hdmi = NULL;
+video_device_alloc_failed:
+       kfree(dev);
+enomem:
+       return ret;
+}
+
+void maru_hdmi_release(void)
+{
+       if (vfd_hdmi) {
+               video_unregister_device(vfd_hdmi);
+               video_device_release(vfd_hdmi);
+       }
+
+       if (dev)
+               kfree(dev);
+
+       vfd_hdmi = NULL;
+       dev = NULL;
+}
diff --git a/drivers/maru/tv/maru_hdmiswitch.c b/drivers/maru/tv/maru_hdmiswitch.c
new file mode 100644 (file)
index 0000000..963a4df
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * MARU Virtual External Input Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * GunSoo Kim <gunsoo83.kim@samsung.com>
+ * HyunJin Lee <hyunjin816.lee@samsung.com>
+ * SangHo Park <sangho1206.park@samsung.com>
+ *
+ * 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
+ *
+ */
+
+/*
+ * Some code based on vivi driver or videobuf_vmalloc.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include "maru_v4l2.h"
+#include "maru_external_input.h"
+
+static unsigned extinput_hdmiswitch_debug = 0;
+module_param(extinput_hdmiswitch_debug, int, 0644);
+MODULE_PARM_DESC(extinput_hdmiswitch_debug, "Turn on/off maru external input switch debugging (default:off).");
+
+#define maru_hdmiswitch_err(fmt, arg...) \
+       printk(KERN_ERR "[ERR]maru_hdmiswitch[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmiswitch_warn(fmt, arg...) \
+       printk(KERN_WARNING "[WARN]maru_hdmiswitch[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmiswitch_info(fmt, arg...) \
+       printk(KERN_INFO "[INFO]maru_hdmiswitch[%s]: " fmt, __func__, ##arg)
+
+#define maru_hdmiswitch_dbg(level, fmt, arg...) \
+       do { \
+               if (extinput_hdmiswitch_debug >= (level)) { \
+                       printk(KERN_ERR "[DBG]maru_hdmiswitch[%s]: " fmt, \
+                                                       __func__, ##arg); \
+               } \
+       } while (0)
+
+#define MARU_EXTINPUT_MODULE_NAME "maru_extinput"
+
+#define MARU_EXTINPUT_MAJOR_VERSION 0
+#define MARU_EXTINPUT_MINOR_VERSION 0
+#define MARU_EXTINPUT_RELEASE 1
+#define MARU_EXTINPUT_VERSION \
+       KERNEL_VERSION(MARU_EXTINPUT_MAJOR_VERSION, \
+                       MARU_EXTINPUT_MINOR_VERSION, MARU_EXTINPUT_RELEASE)
+
+MODULE_DESCRIPTION("MARU Virtual External Input Driver");
+MODULE_AUTHOR("Gunsoo Kim <gunsoo83.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ * Basic structures
+ */
+enum {
+       EXTINPUT_HDMI1,
+       EXTINPUT_HDMI2,
+       EXTINPUT_HDMI3,
+       EXTINPUT_HDMI4,
+       EXTINPUT_COMPOSITE,
+       EXTINPUT_COMPONENT,
+#if 0 /* spec out */
+       EXTINPUT_DVI,
+       EXTINPUT_S_VIDEO,
+       EXTINPUT_DSUB
+#endif
+};
+
+static ssize_t av1_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t comp_show(struct device *dev, struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(ident_av1, 0444, av1_show, NULL);
+static DEVICE_ATTR(ident_comp, 0444, comp_show, NULL);
+struct class *gpio_tv_class;
+struct device *class_dev;
+
+int hdmi_onoff[HDMI_DEV_COUNT] = {1, 1, 1, 1};
+int composite = 0;
+int component = 0;
+/* TODO : modify this variable when implement device expansion */
+int hdmi_switch_num = HDMI_SWITCH_DEVICE;
+int hdmi_num = HDMI_PROCESSOR_DEVICE;
+
+/*
+ * Use only one instance.
+ */
+static struct maru_extinput_device *maru_extinput_instance;
+
+/* get device on/off status */
+int get_hdmi_onoff_status(int index)
+{
+       if (index < 0 || index >= HDMI_DEV_COUNT)
+               return -EINVAL;
+
+       return hdmi_onoff[index];
+}
+
+/* get composite status */
+int get_composite_status()
+{
+       return composite;
+}
+
+/* get component status */
+int get_component_status()
+{
+       return component;
+}
+
+/*
+ * show function show on/off state through sysfs nodes
+ */
+static ssize_t av1_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       sprintf(buf, "%d\n", composite);
+
+       maru_hdmiswitch_dbg(5, "av1(composite) show %s\n", buf);
+
+       return strlen(buf);
+}
+
+static ssize_t comp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       sprintf(buf, "%d\n", component);
+
+       maru_hdmiswitch_dbg(5, "comp show %s\n", buf);
+
+       return strlen(buf);
+}
+
+/*
+ * interrupt handling
+ */
+static void maru_extinput_onoff(struct maru_extinput_device *dev)
+{
+       uint32_t recv_state = 0;
+       uint32_t change_state = 0;
+       uint32_t connect_state = 0;
+       int i = 0;
+
+       recv_state = ioread32(dev->mmregs + MARU_EXTINPUT_ONOFF_STATE);
+       connect_state = recv_state & STATE_CHECKER;
+       change_state = (recv_state >> STATE_SHIFT) & STATE_CHECKER;
+
+       /* on/off processing */
+       for (i = 0; i < MAX_EXTINPUT_COUNT; i++) {
+               if ((change_state >> i) & 0x01) {
+                       switch (i) {
+                       case EXTINPUT_HDMI1:
+                       case EXTINPUT_HDMI2:
+                       case EXTINPUT_HDMI3:
+                       case EXTINPUT_HDMI4:
+                               maru_hdmiswitch_dbg(5, "hdmi %d :%d\n", i+1, (connect_state >> i) & 0x01);
+                               if ((connect_state >> i) & 0x01)
+                                       hdmi_onoff[i] = 0; /* Connected */
+                               else
+                                       hdmi_onoff[i] = 1; /* No Power */
+                               break;
+                       case EXTINPUT_COMPOSITE:
+                               maru_hdmiswitch_dbg(5, "composite :%d\n", (connect_state >> i) & 0x01);
+                               composite = (connect_state >> i) & 0x01;
+                               break;
+                       case EXTINPUT_COMPONENT:
+                               maru_hdmiswitch_dbg(5, "component :%d\n", (connect_state >> i) & 0x01);
+                               component = (connect_state >> i) & 0x01;
+                               break;
+#if 0 /* spec out */
+                       case EXTINPUT_DVI:
+                               maru_hdmiswitch_dbg(5, "dvi :%d\n", (connect_state >> i) & 0x01);
+                               break;
+                       case EXTINPUT_S_VIDEO:
+                               maru_hdmiswitch_dbg(5, "s_video :%d\n", (connect_state >> i) & 0x01);
+                               break;
+                       case EXTINPUT_DSUB:
+                               maru_hdmiswitch_dbg(5, "dsub :%d\n", (connect_state >> i) & 0x01);
+                               break;
+#endif
+                       }
+               }
+       }
+
+}
+
+static irqreturn_t maru_extinput_irq_handler(int irq, void *dev_id)
+{
+       struct maru_extinput_device *dev = dev_id;
+       uint32_t isr = 0;
+       uint32_t state = 0;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       isr = ioread32(dev->mmregs + MARU_EXTINPUT_ISR);
+       if (!isr) {
+               maru_hdmiswitch_dbg(1, "This irq is not for this module\n");
+               return IRQ_NONE;
+       }
+
+       state = ioread32(dev->mmregs + MARU_EXTINPUT_ISR_STATE);
+       switch (state) {
+       case _ISR_EXTINPUT:
+               maru_extinput_onoff(dev);
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *cap)
+{
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       cap->bus_info[0] = 0;
+       cap->version = MARU_EXTINPUT_VERSION;
+       cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+       return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *inp)
+{
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       /* TODO: always true. check again later */
+       inp->status = true;
+
+       return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+       struct maru_extinput_device *dev = priv;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       *i = dev->switch_input;
+
+       return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+       int ret = 0;
+       struct maru_extinput_device *dev = priv;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       if (i >= HDMI_DEV_COUNT)
+               return -EINVAL;
+
+       mutex_lock(&dev->mlock);
+       iowrite32(0, dev->mmregs + MARU_EXTINPUT_DTC);
+       iowrite32(i, dev->mmregs + MARU_EXTINPUT_S_DATA);
+
+       iowrite32(0, dev->mmregs + MARU_SWITCH_S_INPUT);
+       ret = (int)ioread32(dev->mmregs + MARU_SWITCH_S_INPUT);
+       if (ret) {
+               maru_hdmiswitch_err("Failed to set the input value\n");
+               mutex_unlock(&dev->mlock);
+               return -ret;
+       }
+       dev->switch_input = i;
+
+       mutex_unlock(&dev->mlock);
+       return ret;
+}
+
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *ctrls)
+{
+       int i = 0;
+       struct v4l2_ext_control *ctrl = NULL;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_DV) {
+               for (i = 0; i < ctrls->count; i++) {
+                       /* FIXME : dummy */
+                       ctrl = ctrls->controls + i;
+                       switch (ctrl->id) {
+                               case V4L2_CID_DV_G_SWITCH_INFO:
+                                       ctrl->value = 0;
+                                       break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *ctrls)
+{
+       struct v4l2_ext_control *ctrl;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_DV) {
+               ctrl = ctrls->controls;
+               switch (ctrl->id) {
+                       /* FIXME : dummy */
+                       maru_hdmiswitch_dbg(5, "set controls id : %d\n", ctrl->id);
+               }
+       }
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------
+       File operations for the device
+   ------------------------------------------------------------------*/
+static int maru_extinput_open(struct file *file)
+{
+       struct maru_extinput_device *dev = maru_extinput_instance;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+       file->private_data = dev;
+
+       return 0;
+}
+
+static int maru_extinput_close(struct file *file)
+{
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       return 0;
+}
+
+static const struct v4l2_ioctl_ops maru_extinput_ioctl_ops = {
+       .vidioc_querycap                = vidioc_querycap,
+       .vidioc_enum_input              = vidioc_enum_input,
+       .vidioc_g_input                 = vidioc_g_input,
+       .vidioc_s_input                 = vidioc_s_input,
+       .vidioc_g_ext_ctrls             = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls             = vidioc_s_ext_ctrls,
+};
+
+static const struct v4l2_file_operations maru_extinput_fops = {
+       .owner          = THIS_MODULE,
+       .open           = maru_extinput_open,
+       .release        = maru_extinput_close,
+       .unlocked_ioctl         = video_ioctl2,
+};
+
+static struct video_device maru_extinput_video_dev = {
+       .name                   = MARU_EXTINPUT_MODULE_NAME,
+       .fops                   = &maru_extinput_fops,
+       .ioctl_ops              = &maru_extinput_ioctl_ops,
+       .minor                  = -1,
+       .release                = video_device_release,
+};
+
+/* -----------------------------------------------------------------
+       Initialization and module stuff
+   ------------------------------------------------------------------*/
+
+DEFINE_PCI_DEVICE_TABLE(maru_extinput_pci_id_tbl) = {
+       {
+               .vendor         = PCI_VENDOR_ID_TIZEN,
+               .device         = PCI_DEVICE_ID_VIRTUAL_EXTERNAL_INPUT,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(pci, maru_extinput_pci_id_tbl);
+static int maru_extinput_pci_initdev(struct pci_dev *pdev,
+                               const struct pci_device_id *id)
+{
+       int ret_val;
+       struct maru_extinput_device *dev;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       if (maru_extinput_instance != NULL) {
+               maru_hdmiswitch_err("Only one device allowed\n");
+               ret_val = -EBUSY;
+               goto err;
+       }
+
+       dev = kzalloc(sizeof(struct maru_extinput_device), GFP_KERNEL);
+       if (!dev) {
+               maru_hdmiswitch_err("kzalloc() failed\n");
+               ret_val = -ENOMEM;
+               goto dev_kzalloc_failed;
+       }
+       maru_extinput_instance = dev;
+
+       ret_val = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+       if (ret_val) {
+               maru_hdmiswitch_err("video_device_register() failed!!\n");
+               goto video_device_register_failed;
+       }
+
+       dev->pdev = pdev;
+       mutex_init(&dev->mlock);
+       dev->vfd_hdmi_switch = video_device_alloc();
+       if (!dev->vfd_hdmi_switch) {
+               maru_hdmiswitch_err("video_device_alloc() failed!!\n");
+               ret_val = -ENOMEM;
+               goto hdmi_switch_device_alloc_failed;
+       }
+
+       memcpy(dev->vfd_hdmi_switch, &maru_extinput_video_dev, sizeof(maru_extinput_video_dev));
+
+       dev->vfd_hdmi_switch->dev_parent = &dev->pdev->dev;
+       dev->vfd_hdmi_switch->v4l2_dev = &dev->v4l2_dev;
+
+       ret_val = pci_enable_device(dev->pdev);
+       if (ret_val) {
+               maru_hdmiswitch_err("pci_enable_device failed!!\n");
+               goto pci_enable_device_failed;
+       }
+       pci_set_master(dev->pdev);
+
+       ret_val = -EIO;
+       dev->mem_base = pci_resource_start(dev->pdev, 0);
+       dev->mem_size = pci_resource_len(dev->pdev, 0);
+
+       if (!dev->mem_base) {
+               maru_hdmiswitch_err("pci_resource_start failed!!\n");
+               goto mem_base_is_null;
+       }
+
+       if (!request_mem_region(dev->mem_base, dev->mem_size,
+                                               MARU_EXTINPUT_MODULE_NAME)) {
+               maru_hdmiswitch_err("request_mem_region(mem) failed!!\n");
+               goto request_mem_region_mem_base;
+       }
+
+       dev->io_base = pci_resource_start(dev->pdev, 1);
+       dev->io_size = pci_resource_len(dev->pdev, 1);
+
+       if (!dev->io_base) {
+               maru_hdmiswitch_err("pci_resource_start failed!!\n");
+               goto io_base_is_null;
+       }
+
+       if (!request_mem_region(dev->io_base, dev->io_size,
+                                               MARU_EXTINPUT_MODULE_NAME)) {
+               maru_hdmiswitch_err("request_mem_region(io) failed!!\n");
+               goto request_mem_region_io_base;
+       }
+
+       dev->mmregs = ioremap(dev->io_base, dev->io_size);
+       if (!dev->mmregs) {
+               maru_hdmiswitch_err("ioremap failed!!\n");
+               goto ioremap_failed;
+       }
+
+       ret_val = request_irq(dev->pdev->irq, maru_extinput_irq_handler,
+                               IRQF_SHARED, MARU_EXTINPUT_MODULE_NAME, dev);
+       if (ret_val < 0) {
+               maru_hdmiswitch_err("Failed to request the irq: irq(#%d)\n", dev->pdev->irq);
+               goto request_irq_failed;
+       }
+
+       /* register /dev/video28 */
+       ret_val = video_register_device(dev->vfd_hdmi_switch, VFL_TYPE_GRABBER, HDMI_SWITCH_DEVICE);
+       if (ret_val < 0) {
+               maru_hdmiswitch_err("video_register_device failed!!\n");
+               goto register_dev_failed;
+       }
+
+       pci_set_drvdata(pdev, dev);
+
+       snprintf(dev->vfd_hdmi_switch->name, sizeof(dev->vfd_hdmi_switch->name), "%s (%i)",
+                               maru_extinput_video_dev.name, dev->vfd_hdmi_switch->num);
+       maru_hdmiswitch_info("V4L2 device registerd as /dev/video%d\n", dev->vfd_hdmi_switch->num);
+
+       /* initialize hdmi device /dev/video21 */
+       ret_val = maru_hdmi_init(dev);
+       if (ret_val < 0)
+               goto hdmi_init_failed;
+
+       /* initialize composite device /dev/video20 */
+       ret_val = maru_composite_init(dev);
+       if (ret_val < 0)
+               goto composite_init_failed;
+
+       /* initialize composite device /dev/video24 */
+       ret_val = maru_component_init(dev);
+       if (ret_val < 0)
+               goto component_init_failed;
+
+       /* make sysfs */
+       gpio_tv_class = class_create(THIS_MODULE, CLASS_NAME);
+       if (IS_ERR(gpio_tv_class)) {
+               ret_val = PTR_ERR(gpio_tv_class);
+               goto class_create_failed;
+       }
+
+       class_dev = device_create(gpio_tv_class, NULL, 0, NULL, CLASS_NAME);
+       if (IS_ERR(class_dev)) {
+               ret_val = PTR_ERR(class_dev);
+               goto device_create_failed;
+       }
+
+       ret_val = device_create_file(class_dev, &dev_attr_ident_av1);
+       if (ret_val)
+               goto create_file_failed1;
+
+       ret_val = device_create_file(class_dev, &dev_attr_ident_comp);
+       if (ret_val)
+               goto create_file_failed2;
+
+       return 0;
+
+create_file_failed2:
+       device_remove_file(class_dev, &dev_attr_ident_av1);
+create_file_failed1:
+       device_destroy(gpio_tv_class, class_dev->devt);
+device_create_failed:
+       class_destroy(gpio_tv_class);
+class_create_failed:
+       maru_component_release();
+component_init_failed:
+       maru_composite_release();
+composite_init_failed:
+       maru_hdmi_release();
+hdmi_init_failed:
+       video_unregister_device(dev->vfd_hdmi_switch);
+register_dev_failed:
+request_irq_failed:
+       iounmap(dev->mmregs);
+ioremap_failed:
+       release_mem_region(dev->io_base, dev->io_size);
+request_mem_region_io_base:
+io_base_is_null:
+       release_mem_region(dev->mem_base, dev->mem_size);
+request_mem_region_mem_base:
+mem_base_is_null:
+       pci_disable_device(dev->pdev);
+pci_enable_device_failed:
+       video_device_release(dev->vfd_hdmi_switch);
+hdmi_switch_device_alloc_failed:
+       v4l2_device_unregister(&dev->v4l2_dev);
+video_device_register_failed:
+       kfree(dev);
+       dev = NULL;
+       maru_extinput_instance = NULL;
+dev_kzalloc_failed:
+err:
+       return ret_val;
+}
+
+static void maru_extinput_pci_removedev(struct pci_dev *pdev)
+{
+       struct maru_extinput_device *dev = pci_get_drvdata(pdev);
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       if (dev == NULL) {
+               maru_hdmiswitch_warn("pci_remove on unknown pdev %p.\n", pdev);
+               return;
+       }
+
+       free_irq(pdev->irq, dev);
+
+       device_remove_file(class_dev, &dev_attr_ident_comp);
+       device_remove_file(class_dev, &dev_attr_ident_av1);
+       device_destroy(gpio_tv_class, class_dev->devt);
+       class_destroy(gpio_tv_class);
+
+       maru_component_release();
+       maru_composite_release();
+       maru_hdmi_release();
+
+       video_unregister_device(dev->vfd_hdmi_switch);
+       if (dev->mmregs) {
+               iounmap(dev->mmregs);
+               dev->mmregs = 0;
+       }
+
+       if (dev->io_base) {
+               release_mem_region(dev->io_base, dev->io_size);
+               dev->io_base = 0;
+       }
+       if (dev->mem_base) {
+               release_mem_region(dev->mem_base, dev->mem_size);
+               dev->mem_base = 0;
+       }
+       pci_disable_device(dev->pdev);
+       v4l2_device_unregister(&dev->v4l2_dev);
+       kfree(dev);
+       dev = NULL;
+       maru_extinput_instance = NULL;
+}
+
+static struct pci_driver maru_extinput_pci_driver = {
+       .name           = MARU_EXTINPUT_MODULE_NAME,
+       .id_table       = maru_extinput_pci_id_tbl,
+       .probe          = maru_extinput_pci_initdev,
+       .remove         = maru_extinput_pci_removedev,
+};
+
+static int __init maru_extinput_init(void)
+{
+       int retv = 0;
+
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       retv = pci_register_driver(&maru_extinput_pci_driver);
+       if (retv < 0) {
+               maru_hdmiswitch_err("Error %d while loading maru_extinput driver\n", retv);
+               return retv;
+       }
+
+       maru_hdmiswitch_info("MARU External Input Driver ver %u.%u.%u successfully loaded.\n",
+               (MARU_EXTINPUT_VERSION >> 16) & 0xFF, (MARU_EXTINPUT_VERSION >> 8) & 0xFF,
+               MARU_EXTINPUT_VERSION & 0xFF);
+
+       return retv;
+}
+
+static void __exit maru_extinput_exit(void)
+{
+       maru_hdmiswitch_dbg(5, "[extinput]enter\n");
+
+       pci_unregister_driver(&maru_extinput_pci_driver);
+}
+
+module_init(maru_extinput_init);
+module_exit(maru_extinput_exit);
index 6c77144..1b3e421 100644 (file)
 
 #define PCI_VENDOR_ID_SAMSUNG          0x144d
 
+#define PCI_DEVICE_ID_VIRTUAL_EXTERNAL_INPUT   0x1019
 #define PCI_DEVICE_ID_VIRTUAL_TUNER            0x1044
 #define PCI_DEVICE_ID_VIRTUAL_DTV_DECODER      0x1048
 #define PCI_DEVICE_ID_VIRTUAL_DTV_AUDIO                0x104C