summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYunHo Lee <yun117.lee@samsung.com>2016-07-21 13:25:28 +0900
committerSeokYeon Hwang <syeon.hwang@samsung.com>2016-07-26 11:17:20 +0900
commitbea011fecd31ebd5c64cb29595a5996ee04b9cad (patch)
tree77c48549fc1a2eef2e9f5c23e5bc6ef1e1cd5478
parenteb79ce382737b75a46a4e46c4111bf6e5d3606dd (diff)
downloademulator-kernel-bea011fecd31ebd5c64cb29595a5996ee04b9cad.tar.gz
emulator-kernel-bea011fecd31ebd5c64cb29595a5996ee04b9cad.tar.bz2
emulator-kernel-bea011fecd31ebd5c64cb29595a5996ee04b9cad.zip
TV: add EXTERNAL_INPUT
Change-Id: I0bc94f28d2b9dca6bd797582541a3c2e0986d178 Signed-off-by: YunHo Lee <yun117.lee@samsung.com>
-rw-r--r--arch/x86/configs/tizen_emul_defconfig1
-rw-r--r--drivers/maru/Kconfig5
-rw-r--r--drivers/maru/tv/Makefile1
-rw-r--r--drivers/maru/tv/maru_component.c541
-rw-r--r--drivers/maru/tv/maru_composite.c736
-rw-r--r--drivers/maru/tv/maru_external_input.h259
-rw-r--r--drivers/maru/tv/maru_hdmi.c772
-rw-r--r--drivers/maru/tv/maru_hdmiswitch.c670
-rw-r--r--include/linux/pci_ids.h1
9 files changed, 2986 insertions, 0 deletions
diff --git a/arch/x86/configs/tizen_emul_defconfig b/arch/x86/configs/tizen_emul_defconfig
index 2644db982cbb..a306c1df3a55 100644
--- a/arch/x86/configs/tizen_emul_defconfig
+++ b/arch/x86/configs/tizen_emul_defconfig
@@ -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
diff --git a/drivers/maru/Kconfig b/drivers/maru/Kconfig
index 5bf0d4d47734..d2d0a3b84670 100644
--- a/drivers/maru/Kconfig
+++ b/drivers/maru/Kconfig
@@ -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
diff --git a/drivers/maru/tv/Makefile b/drivers/maru/tv/Makefile
index ff6c799e3647..aa42be795641 100644
--- a/drivers/maru/tv/Makefile
+++ b/drivers/maru/tv/Makefile
@@ -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
index 000000000000..07c7dde904c4
--- /dev/null
+++ b/drivers/maru/tv/maru_component.c
@@ -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
index 000000000000..bc4703c09932
--- /dev/null
+++ b/drivers/maru/tv/maru_composite.c
@@ -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
index 000000000000..710aba34b199
--- /dev/null
+++ b/drivers/maru/tv/maru_external_input.h
@@ -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
index 000000000000..970c81d4524b
--- /dev/null
+++ b/drivers/maru/tv/maru_hdmi.c
@@ -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
index 000000000000..963a4df74067
--- /dev/null
+++ b/drivers/maru/tv/maru_hdmiswitch.c
@@ -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);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 6c771445a19d..1b3e421b834e 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2098,6 +2098,7 @@
#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