diff options
author | YunHo Lee <yun117.lee@samsung.com> | 2016-07-21 13:25:28 +0900 |
---|---|---|
committer | SeokYeon Hwang <syeon.hwang@samsung.com> | 2016-07-26 11:17:20 +0900 |
commit | bea011fecd31ebd5c64cb29595a5996ee04b9cad (patch) | |
tree | 77c48549fc1a2eef2e9f5c23e5bc6ef1e1cd5478 | |
parent | eb79ce382737b75a46a4e46c4111bf6e5d3606dd (diff) | |
download | emulator-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_defconfig | 1 | ||||
-rw-r--r-- | drivers/maru/Kconfig | 5 | ||||
-rw-r--r-- | drivers/maru/tv/Makefile | 1 | ||||
-rw-r--r-- | drivers/maru/tv/maru_component.c | 541 | ||||
-rw-r--r-- | drivers/maru/tv/maru_composite.c | 736 | ||||
-rw-r--r-- | drivers/maru/tv/maru_external_input.h | 259 | ||||
-rw-r--r-- | drivers/maru/tv/maru_hdmi.c | 772 | ||||
-rw-r--r-- | drivers/maru/tv/maru_hdmiswitch.c | 670 | ||||
-rw-r--r-- | include/linux/pci_ids.h | 1 |
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 |