TV: Add Tuner driver
authorYunHo Lee <yun117.lee@samsung.com>
Wed, 13 Jul 2016 06:20:02 +0000 (15:20 +0900)
committerYunHo Lee <yun117.lee@samsung.com>
Wed, 20 Jul 2016 01:38:10 +0000 (10:38 +0900)
Change-Id: I31cc002a4903fe46950526341782b8a80a8c9936
Signed-off-by: YunHo Lee <yun117.lee@samsung.com>
14 files changed:
arch/x86/configs/tizen_emul_defconfig
drivers/maru/Kconfig
drivers/maru/tv/Makefile
drivers/maru/tv/maru_atv.c [new file with mode: 0644]
drivers/maru/tv/maru_atv.h [new file with mode: 0644]
drivers/maru/tv/maru_dmxdev.c [new file with mode: 0644]
drivers/maru/tv/maru_dtv_decoder.c [new file with mode: 0644]
drivers/maru/tv/maru_sif.c [new file with mode: 0644]
drivers/maru/tv/maru_sif.h [new file with mode: 0644]
drivers/maru/tv/maru_tuner.c [new file with mode: 0644]
drivers/maru/tv/maru_tuner.h [new file with mode: 0644]
drivers/maru/tv/maru_v4l2.h [new file with mode: 0644]
drivers/media/dvb-core/demux.h
include/linux/pci_ids.h

index 61dafc64bb5c01d851365aac35dc1cb1bef08f12..1cfe3372db479a88e3488e91c52cf8caf5836326 100644 (file)
@@ -2555,7 +2555,7 @@ CONFIG_MEDIA_SUPPORT=y
 #
 CONFIG_MEDIA_CAMERA_SUPPORT=y
 # CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set
-# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
 # CONFIG_MEDIA_RADIO_SUPPORT is not set
 # CONFIG_MEDIA_SDR_SUPPORT is not set
 # CONFIG_MEDIA_RC_SUPPORT is not set
@@ -2566,7 +2566,11 @@ CONFIG_VIDEO_V4L2=y
 # CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
 CONFIG_VIDEOBUF_GEN=y
 CONFIG_VIDEOBUF_VMALLOC=y
+CONFIG_DVB_CORE=y
+CONFIG_DVB_NET=y
 # CONFIG_TTPCI_EEPROM is not set
+CONFIG_DVB_MAX_ADAPTERS=8
+# CONFIG_DVB_DYNAMIC_MINORS is not set
 
 #
 # Media drivers
@@ -2580,11 +2584,31 @@ CONFIG_MEDIA_PCI_SUPPORT=y
 # CONFIG_VIDEO_SOLO6X10 is not set
 # CONFIG_VIDEO_TW68 is not set
 # CONFIG_VIDEO_ZORAN is not set
+
+#
+# Media capture/analog/hybrid TV support
+#
+# CONFIG_VIDEO_CX25821 is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_SAA7164 is not set
+
+#
+# Media digital TV PCI Adapters
+#
+# CONFIG_DVB_AV7110 is not set
+# CONFIG_DVB_BUDGET_CORE is not set
+# CONFIG_DVB_B2C2_FLEXCOP_PCI is not set
+# CONFIG_DVB_PLUTO2 is not set
+# CONFIG_DVB_PT1 is not set
+# CONFIG_DVB_PT3 is not set
+# CONFIG_DVB_NGENE is not set
+# CONFIG_DVB_DDBRIDGE is not set
 CONFIG_V4L_PLATFORM_DRIVERS=y
 # CONFIG_VIDEO_CAFE_CCIC is not set
 # CONFIG_SOC_CAMERA is not set
 # CONFIG_V4L_MEM2MEM_DRIVERS is not set
 # CONFIG_V4L_TEST_DRIVERS is not set
+# CONFIG_DVB_PLATFORM_DRIVERS is not set
 
 #
 # Supported MMC/SDIO adapters
@@ -2595,6 +2619,7 @@ CONFIG_V4L_PLATFORM_DRIVERS=y
 # Media ancillary drivers (tuners, sensors, i2c, frontends)
 #
 CONFIG_MEDIA_SUBDRV_AUTOSELECT=y
+CONFIG_MEDIA_ATTACH=y
 
 #
 # Audio decoders, processors and mixers
@@ -2639,6 +2664,58 @@ CONFIG_MEDIA_SUBDRV_AUTOSELECT=y
 #
 # Sensors used on soc_camera driver
 #
+CONFIG_MEDIA_TUNER=y
+CONFIG_MEDIA_TUNER_SIMPLE=y
+CONFIG_MEDIA_TUNER_TDA8290=y
+CONFIG_MEDIA_TUNER_TDA827X=y
+CONFIG_MEDIA_TUNER_TDA18271=y
+CONFIG_MEDIA_TUNER_TDA9887=y
+CONFIG_MEDIA_TUNER_MT20XX=y
+CONFIG_MEDIA_TUNER_XC2028=y
+CONFIG_MEDIA_TUNER_XC5000=y
+CONFIG_MEDIA_TUNER_XC4000=y
+CONFIG_MEDIA_TUNER_MC44S803=y
+
+#
+# Multistandard (satellite) frontends
+#
+
+#
+# Multistandard (cable + terrestrial) frontends
+#
+
+#
+# DVB-S (satellite) frontends
+#
+
+#
+# DVB-T (terrestrial) frontends
+#
+# CONFIG_DVB_AS102_FE is not set
+
+#
+# DVB-C (cable) frontends
+#
+
+#
+# ATSC (North American/Korean Terrestrial/Cable DTV) frontends
+#
+
+#
+# ISDB-T (terrestrial) frontends
+#
+
+#
+# ISDB-S (satellite) & ISDB-T (terrestrial) frontends
+#
+
+#
+# Digital terrestrial only tuners/PLL
+#
+
+#
+# SEC control devices for DVB-S
+#
 
 #
 # Tools to develop new frontends
@@ -3534,6 +3611,7 @@ CONFIG_MARU_VDPRAM=y
 CONFIG_MARU_VIRTIO_VMODEM=y
 CONFIG_MARU_VIRTIO_ROTARY=y
 CONFIG_MARU_TV=y
+CONFIG_MARU_TV_TUNER=y
 CONFIG_MARU_TV_EEPROM=y
 CONFIG_MARU_TV_DUMMY=y
 CONFIG_MARU_TV_MICOM_MSG=y
index b3ab0b5d0515777c35a93987667e9b09a6e82ff5..5bf0d4d47734c5cf01a862735907a5e7c64367c9 100644 (file)
@@ -71,6 +71,10 @@ config MARU_TV
        depends on MARU != n
        default n
 
+config MARU_TV_TUNER
+       tristate "MARU Tuner Driver"
+       depends on MARU_TV != n && DVB_CORE && PCI
+
 config MARU_TV_EEPROM
        tristate "MARU VirtIO Virtual EEPROM Device Driver"
        depends on MARU_TV != n
index 04c14603f583997555e36e6115b894d6b02549f0..ff6c799e36474b62baf2313191ccaac237c26996 100644 (file)
@@ -7,3 +7,4 @@ 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_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_atv.c b/drivers/maru/tv/maru_atv.c
new file mode 100644 (file)
index 0000000..3ee2ef9
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * MARU Virtual Dummy ATV
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@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
+ *
+ */
+
+/*
+ * This driver is audio/sif of ATV without virtual qemu device
+ * If the virtual device is required, should implement followings additioally.
+ *
+ * - driver initialization should separates with maru_tuner driver
+ * - v4l2 and video device should get parent device from driver probing not from maru_tuner
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include "maru_atv.h"
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off maru atv dummy debugging (default:off).");
+
+#define print_err(fmt, arg...) \
+       printk(KERN_ERR "maruatv: (%s) " fmt, __func__, ##arg)
+
+#define print_warn(fmt, arg...) \
+       printk(KERN_WARNING "maruatv: (%s) " fmt, __func__, ##arg)
+
+#define print_info(fmt, arg...) \
+       printk(KERN_INFO "maruatv: (%s) " fmt, __func__, ##arg)
+
+#define print_dbg(level, fmt, arg...) \
+       do { \
+               if (debug >= (level)) { \
+                       printk(KERN_INFO "maruatv: (%s:%d) " fmt, \
+                                       __func__, __LINE__, ##arg); \
+               } \
+       } while (0)
+
+struct maruatv_dev {
+       struct miscdevice *mdev;
+       struct video_device *video_audio;
+       struct video_device *video_sif;
+       struct v4l2_device v4l2_audio;
+       struct v4l2_device v4l2_sif;
+};
+
+static struct maruatv_dev *maruatv;
+
+/*
+ * common dummy fops
+ */
+static int maruatv_dummy_open(struct file *file)
+{
+       print_dbg(1, "\n");
+       return 0;
+}
+
+static int maruatv_dummy_release(struct file *file)
+{
+       print_dbg(1, "\n");
+       return 0;
+}
+
+static const struct v4l2_file_operations maruatv_dummy_fops = {
+       .owner = THIS_MODULE,
+       .open = maruatv_dummy_open,
+       .release = maruatv_dummy_release,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+/*
+ * atv audio
+ */
+static int vidioc_enumaudio_audio(struct file *file, void *fh, struct v4l2_audio *audio)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_g_audio_audio(struct file *file, void *fh, struct v4l2_audio *audio)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_s_audio_audio(struct file *file, void *fh, const struct v4l2_audio *audio)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_enumaudout_audio(struct file *file, void *fh,        struct v4l2_audioout *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_g_audout_audio(struct file *file, void *fh, struct v4l2_audioout *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+static int vidioc_s_audout_audio(struct file *file, void *fh, const struct v4l2_audioout *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+static int vidioc_g_modulator_audio(struct file *file, void *fh, struct v4l2_modulator *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+static int vidioc_s_modulator_audio(struct file *file, void *fh, const struct v4l2_modulator *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_g_ext_ctrls_audio(struct file *file, void *fh, struct v4l2_ext_controls * ctrls)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_g_enc_index_audio(struct file *file, void *fh,  struct v4l2_enc_idx *idx)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_encoder_cmd_audio(struct file *file, void *fh,  struct v4l2_encoder_cmd *enc)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_try_encoder_cmd_audio(struct file *file, void *fh,  struct v4l2_encoder_cmd *enc)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+/* v4l2_ioctl ops for audio */
+ struct v4l2_ioctl_ops maruatv_audio_ioctl_ops = {
+       .vidioc_enumaudio = vidioc_enumaudio_audio,
+       .vidioc_g_audio = vidioc_g_audio_audio,
+       .vidioc_s_audio = vidioc_s_audio_audio,
+       .vidioc_enumaudout = vidioc_enumaudout_audio,
+       .vidioc_g_audout = vidioc_g_audout_audio,
+       .vidioc_s_audout = vidioc_s_audout_audio,
+       .vidioc_g_modulator = vidioc_g_modulator_audio,
+       .vidioc_s_modulator = vidioc_s_modulator_audio,
+       .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls_audio,
+       .vidioc_g_enc_index = vidioc_g_enc_index_audio,
+       .vidioc_encoder_cmd = vidioc_encoder_cmd_audio,
+       .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd_audio,
+};
+
+const struct v4l2_ioctl_ops *get_audio_v4l2_ioctl_ops(void)
+{
+       return &maruatv_audio_ioctl_ops;
+}
+
+/*
+ * sif
+ */
+static int vidioc_querycap_sif(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_enumaudio_sif(struct file *file, void *fh, struct v4l2_audio *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_g_audio_sif(struct file *file, void *fh, struct v4l2_audio *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_s_audio_sif(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+static int vidioc_streamon_sif(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+
+static int vidioc_streamoff_sif(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+       print_dbg(1, "%s is called", __func__);
+       return 0;
+}
+
+/* v4l2_ioctl ops for sif */
+struct v4l2_ioctl_ops maruatv_sif_ioctl_ops = {
+       .vidioc_querycap = vidioc_querycap_sif,
+       .vidioc_enumaudio = vidioc_enumaudio_sif,
+       .vidioc_g_audio = vidioc_g_audio_sif,
+       .vidioc_s_audio = vidioc_s_audio_sif,
+       .vidioc_enumaudout = NULL,
+       .vidioc_g_audout = NULL,
+       .vidioc_s_audout = NULL,
+       .vidioc_g_modulator = NULL,
+       .vidioc_s_modulator = NULL,
+       .vidioc_g_enc_index = NULL,
+       .vidioc_encoder_cmd = NULL,
+       .vidioc_try_encoder_cmd = NULL,
+       .vidioc_streamon = vidioc_streamon_sif,
+       .vidioc_streamoff = vidioc_streamoff_sif,
+};
+
+const struct v4l2_ioctl_ops *get_sif_v4l2_ioctl_ops(void)
+{
+       return &maruatv_sif_ioctl_ops;
+}
+
+int maruatv_audio_init(void)
+{
+       int ret = 0;
+
+       ret = v4l2_device_register(NULL, &maruatv->v4l2_audio);
+       if (ret) {
+               print_err("failed to register v4l2 audio device\n");
+               goto err_v4l2_register;
+       }
+
+       maruatv->video_audio = video_device_alloc();
+       if (!maruatv->video_audio) {
+               print_err("failed to allocate video audio device\n");
+               ret = -ENOMEM;
+               goto err_video_device_alloc;
+       }
+
+       maruatv->video_audio->fops = &maruatv_dummy_fops;
+       maruatv->video_audio->ioctl_ops = get_audio_v4l2_ioctl_ops();
+       maruatv->video_audio->release = video_device_release;
+       maruatv->video_audio->tvnorms = V4L2_STD_NTSC;  /* The target supports only NTSC */
+       //maruatv->video_audio->dev_parent = &video_audio->dev;
+       maruatv->video_audio->v4l2_dev = &maruatv->v4l2_audio;
+       strcpy(maruatv->video_audio->name, "maruatv_audio");
+       /* if need really, then should make a device */
+       video_set_drvdata(maruatv->video_audio, maruatv);
+
+       ret = video_register_device(maruatv->video_audio, VFL_TYPE_GRABBER, 0);
+       if (ret) {
+               print_err("failed to register video auido device\n");
+               goto err_video_register;
+       }
+
+       return ret;
+
+err_video_register:
+       kfree(maruatv->video_audio);
+       maruatv->video_audio = NULL;
+err_video_device_alloc:
+       v4l2_device_unregister(&maruatv->v4l2_audio);
+err_v4l2_register:
+
+       return ret;
+}
+
+int maruatv_sif_init(void)
+{
+       int ret = 0;
+
+       ret = v4l2_device_register(NULL, &maruatv->v4l2_sif);
+       if (ret) {
+               print_err("failed to register vl42 sif device\n");
+               goto err_v4l2_register;
+       }
+
+       maruatv->video_sif = video_device_alloc();
+       if (!maruatv->video_sif) {
+               print_err("failed to allocate video sif device\n");
+               ret = -ENOMEM;
+               goto err_video_device_alloc;
+       }
+
+       maruatv->video_sif->fops = &maruatv_dummy_fops;
+       maruatv->video_sif->ioctl_ops = get_sif_v4l2_ioctl_ops();
+       maruatv->video_sif->release = video_device_release;
+       maruatv->video_sif->tvnorms = V4L2_STD_NTSC;    /* The target supports only NTSC */
+       //maruatv->video_sif->dev_parent = &_video_sif->dev;
+       maruatv->video_sif->v4l2_dev = &maruatv->v4l2_sif;
+       strcpy(maruatv->video_sif->name, "maruatv_sif");
+       /* if need really, then should make a device */
+       video_set_drvdata(maruatv->video_sif, maruatv);
+
+       ret = video_register_device(maruatv->video_sif, VFL_TYPE_GRABBER, 1);
+       if (ret) {
+               print_err("failed to register video sif device\n");
+               goto err_video_register;
+       }
+
+       return ret;
+
+err_video_register:
+       kfree(maruatv->video_sif);
+       maruatv->video_sif = NULL;
+err_video_device_alloc:
+       v4l2_device_unregister(&maruatv->v4l2_sif);
+err_v4l2_register:
+
+       return ret;
+}
+
+void maruatv_cleanup(void)
+{
+       if (maruatv->video_audio) {
+               if (video_is_registered(maruatv->video_audio))
+                       video_unregister_device(maruatv->video_audio);
+               else
+                       video_device_release(maruatv->video_audio);
+               maruatv->video_audio = NULL;
+       }
+       if (maruatv->video_sif) {
+               if (video_is_registered(maruatv->video_sif))
+                       video_unregister_device(maruatv->video_sif);
+               else
+                       video_device_release(maruatv->video_sif);
+               maruatv->video_sif = NULL;
+       }
+       if (maruatv) {
+               kfree(maruatv);
+               maruatv = NULL;
+       }
+}
+
+static struct miscdevice maruatv_misc = {
+       MISC_DYNAMIC_MINOR,
+       "atv_audio",
+};
+
+static int __init maruatv_init(void)
+{
+       int ret;
+
+       maruatv = kzalloc(sizeof(struct maruatv_dev), GFP_KERNEL);
+       if (!maruatv) {
+               print_err("failed to alloc maruatv_dev\n");
+               return -ENOMEM;
+       }
+
+       maruatv->mdev = &maruatv_misc;
+       strcpy(maruatv->v4l2_audio.name, "maruatv audio device");
+       strcpy(maruatv->v4l2_sif.name, "maruatv sif device");
+
+       ret = maruatv_audio_init();
+       if (ret < 0) {
+               print_err("failed to maruatv_audio_init\n");
+               goto err_audio_init;
+       }
+
+       ret = maruatv_sif_init();
+       if (ret < 0) {
+               print_err("failed to maruatv_sif_init\n");
+               goto err_sif_init;
+       }
+
+       ret = misc_register(&maruatv_misc);
+       if (ret < 0) {
+               print_err("failed to misc_register on minor=%d\n",
+                       MISC_DYNAMIC_MINOR);
+               goto err_misc_register;
+       }
+
+       print_info("successfully initialized\n");
+
+       return ret;
+
+err_audio_init:
+err_sif_init:
+err_misc_register:
+       maruatv_cleanup();
+
+       return ret;
+}
+
+static void __exit maruatv_exit(void)
+{
+       misc_deregister(&maruatv_misc);
+       maruatv_cleanup();
+}
+
+
+module_init(maruatv_init);
+module_exit(maruatv_exit);
diff --git a/drivers/maru/tv/maru_atv.h b/drivers/maru/tv/maru_atv.h
new file mode 100644 (file)
index 0000000..0e1377a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * MARU Virtual Dummy ATV Header
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@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
+ *
+ */
+
+#ifndef __MARU_ATV_DUMMY_H
+#define __MARU_ATV_DUMMY_H
+
+int maruatv_audio_init(void);
+int maruatv_sif_init(void);
+void maruatv_cleanup(void);
+
+#endif /* #ifndef __MARU_ATV_DUMMY_H */
diff --git a/drivers/maru/tv/maru_dmxdev.c b/drivers/maru/tv/maru_dmxdev.c
new file mode 100644 (file)
index 0000000..f319579
--- /dev/null
@@ -0,0 +1,1361 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                   for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+
+#include "maru_tuner.h"
+
+static int dmxdev_debug;
+
+module_param(dmxdev_debug, int, 0644);
+MODULE_PARM_DESC(dmxdev_debug, "Turn on/off debugging (default:off).");
+
+#define dprintk        if (dmxdev_debug) printk
+
+static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
+                                  const u8 *src, size_t len)
+{
+       ssize_t free;
+
+       if (!len)
+               return 0;
+       if (!buf->data)
+               return 0;
+
+       free = dvb_ringbuffer_free(buf);
+       if (len > free) {
+               dprintk("dmxdev: buffer overflow\n");
+               return -EOVERFLOW;
+       }
+
+       return dvb_ringbuffer_write(buf, src, len);
+}
+
+static void handle_deferred_request(struct dmxdev *dmxdev)
+{
+       struct marutuner_dev *marutuner = container_of(dmxdev, struct marutuner_dev, dmxdev);
+       struct dvb_ringbuffer *src = &dmxdev->dvr_buffer;
+       unsigned long flags;
+       unsigned int status = 0;
+
+       if (!marutuner->defer_request)
+               return;
+
+       spin_lock_irqsave(&marutuner->slock, flags);
+       if (marutuner->defer_request &&
+                       (dvb_ringbuffer_free(src) >= MARUTUNER_MIN_BUFFER_SIZE ||
+                        dvb_ringbuffer_avail(src) == 0)) {
+               marutuner->defer_request = 0;
+               dprintk("[%s] free = %d\n", __func__, (int)dvb_ringbuffer_free(src));
+
+               /* if the dma isn't working, should not request for dma start */
+               status = ioread32(marutuner->io_mem + MARUTUNER_START);
+               if (status)
+                       iowrite32(1, marutuner->io_mem + MARUTUNER_START);
+       }
+       spin_unlock_irqrestore(&marutuner->slock, flags);
+}
+
+static ssize_t marutuner_dmxdev_buffer_read(struct dmxdev *dmxdev,
+                                     int non_blocking, char __user *buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct dvb_ringbuffer *src = &dmxdev->dvr_buffer;
+       size_t todo;
+       ssize_t avail;
+       ssize_t ret = 0;
+
+       if (!src->data)
+               return 0;
+
+       if (src->error) {
+               ret = src->error;
+               dvb_ringbuffer_flush(src);
+               return ret;
+       }
+
+       for (todo = count; todo > 0; todo -= ret) {
+               if (non_blocking && dvb_ringbuffer_empty(src)) {
+                       ret = -EWOULDBLOCK;
+                       break;
+               }
+
+               ret = wait_event_interruptible(src->queue,
+                                              !dvb_ringbuffer_empty(src) ||
+                                              (src->error != 0));
+               if (ret < 0)
+                       break;
+
+               if (src->error) {
+                       ret = src->error;
+                       dvb_ringbuffer_flush(src);
+                       break;
+               }
+
+               avail = dvb_ringbuffer_avail(src);
+               if (avail > todo)
+                       avail = todo;
+
+               ret = dvb_ringbuffer_read_user(src, buf, avail);
+               if (ret < 0)
+                       break;
+
+               buf += ret;
+
+               handle_deferred_request(dmxdev);
+       }
+
+       return (count - todo) ? (count - todo) : ret;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+                                     int non_blocking, char __user *buf,
+                                     size_t count, loff_t *ppos)
+{
+       size_t todo;
+       ssize_t avail;
+       ssize_t ret = 0;
+
+       if (!src->data)
+               return 0;
+
+       if (src->error) {
+               ret = src->error;
+               dvb_ringbuffer_flush(src);
+               return ret;
+       }
+
+       for (todo = count; todo > 0; todo -= ret) {
+               if (non_blocking && dvb_ringbuffer_empty(src)) {
+                       ret = -EWOULDBLOCK;
+                       break;
+               }
+
+               ret = wait_event_interruptible(src->queue,
+                                              !dvb_ringbuffer_empty(src) ||
+                                              (src->error != 0));
+               if (ret < 0)
+                       break;
+
+               if (src->error) {
+                       ret = src->error;
+                       dvb_ringbuffer_flush(src);
+                       break;
+               }
+
+               avail = dvb_ringbuffer_avail(src);
+               if (avail > todo)
+                       avail = todo;
+
+               ret = dvb_ringbuffer_read_user(src, buf, avail);
+               if (ret < 0)
+                       break;
+
+               buf += ret;
+       }
+
+       return (count - todo) ? (count - todo) : ret;
+}
+
+static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type)
+{
+       struct list_head *head, *pos;
+
+       head = demux->get_frontends(demux);
+       if (!head)
+               return NULL;
+       list_for_each(pos, head)
+               if (DMX_FE_ENTRY(pos)->source == type)
+                       return DMX_FE_ENTRY(pos);
+
+       return NULL;
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       struct dmx_frontend *front;
+
+       dprintk("function : %s\n", __func__);
+
+       if (mutex_lock_interruptible(&dmxdev->mutex))
+               return -ERESTARTSYS;
+
+       if (dmxdev->exit) {
+               mutex_unlock(&dmxdev->mutex);
+               return -ENODEV;
+       }
+
+       if ((file->f_flags & O_ACCMODE) == O_RDWR) {
+               if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -EOPNOTSUPP;
+               }
+       }
+
+       if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+               void *mem;
+               if (!dvbdev->readers) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -EBUSY;
+               }
+               mem = vmalloc(DVR_BUFFER_SIZE);
+               if (!mem) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ENOMEM;
+               }
+               dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+               dvbdev->readers--;
+
+               handle_deferred_request(dmxdev);
+       }
+
+       if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+               dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
+
+               if (!dmxdev->demux->write) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -EOPNOTSUPP;
+               }
+
+               front = get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+               if (!front) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -EINVAL;
+               }
+               dmxdev->demux->disconnect_frontend(dmxdev->demux);
+               dmxdev->demux->connect_frontend(dmxdev->demux, front);
+       }
+       dvbdev->users++;
+       mutex_unlock(&dmxdev->mutex);
+       return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       struct marutuner_dev *marutuner = container_of(dmxdev, struct marutuner_dev, dmxdev);
+       unsigned long flags;
+
+       mutex_lock(&dmxdev->mutex);
+
+       if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+               dmxdev->demux->disconnect_frontend(dmxdev->demux);
+               dmxdev->demux->connect_frontend(dmxdev->demux,
+                                               dmxdev->dvr_orig_fe);
+       }
+       if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+               dvbdev->readers++;
+               if (dmxdev->dvr_buffer.data) {
+                       void *mem = dmxdev->dvr_buffer.data;
+                       mb();
+                       spin_lock_irq(&dmxdev->lock);
+                       dmxdev->dvr_buffer.data = NULL;
+                       spin_unlock_irq(&dmxdev->lock);
+                       vfree(mem);
+               }
+               spin_lock_irqsave(&marutuner->slock, flags);
+               marutuner->defer_request = 0;
+               spin_unlock_irqrestore(&marutuner->slock, flags);
+       }
+       /* TODO */
+       dvbdev->users--;
+       if (dvbdev->users == 1 && dmxdev->exit == 1) {
+               fops_put(file->f_op);
+               file->f_op = NULL;
+               mutex_unlock(&dmxdev->mutex);
+               wake_up(&dvbdev->wait_queue);
+       } else
+               mutex_unlock(&dmxdev->mutex);
+
+       return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       int ret;
+
+       if (!dmxdev->demux->write)
+               return -EOPNOTSUPP;
+       if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+               return -EINVAL;
+       if (mutex_lock_interruptible(&dmxdev->mutex))
+               return -ERESTARTSYS;
+
+       if (dmxdev->exit) {
+               mutex_unlock(&dmxdev->mutex);
+               return -ENODEV;
+       }
+       ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+       mutex_unlock(&dmxdev->mutex);
+       return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+                           loff_t *ppos)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+
+       if (dmxdev->exit)
+               return -ENODEV;
+
+       return marutuner_dmxdev_buffer_read(dmxdev,
+                                     file->f_flags & O_NONBLOCK,
+                                     buf, count, ppos);
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+                                     unsigned long size)
+{
+       struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+       void *newmem;
+       void *oldmem;
+
+       dprintk("function : %s\n", __func__);
+
+       if (buf->size == size)
+               return 0;
+       if (!size)
+               return -EINVAL;
+
+       newmem = vmalloc(size);
+       if (!newmem)
+               return -ENOMEM;
+
+       oldmem = buf->data;
+
+       spin_lock_irq(&dmxdev->lock);
+       buf->data = newmem;
+       buf->size = size;
+
+       /* reset and not flush in case the buffer shrinks */
+       dvb_ringbuffer_reset(buf);
+       handle_deferred_request(dmxdev);
+       spin_unlock_irq(&dmxdev->lock);
+
+       vfree(oldmem);
+
+       return 0;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
+                                              *dmxdevfilter, int state)
+{
+       spin_lock_irq(&dmxdevfilter->dev->lock);
+       dmxdevfilter->state = state;
+       spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
+                                     unsigned long size)
+{
+       struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+       void *newmem;
+       void *oldmem;
+
+       if (buf->size == size)
+               return 0;
+       if (!size)
+               return -EINVAL;
+       if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+               return -EBUSY;
+
+       newmem = vmalloc(size);
+       if (!newmem)
+               return -ENOMEM;
+
+       oldmem = buf->data;
+
+       spin_lock_irq(&dmxdevfilter->dev->lock);
+       buf->data = newmem;
+       buf->size = size;
+
+       /* reset and not flush in case the buffer shrinks */
+       dvb_ringbuffer_reset(buf);
+       spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+       vfree(oldmem);
+
+       return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(unsigned long data)
+{
+       struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
+
+       dmxdevfilter->buffer.error = -ETIMEDOUT;
+       spin_lock_irq(&dmxdevfilter->dev->lock);
+       dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
+       spin_unlock_irq(&dmxdevfilter->dev->lock);
+       wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+       struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec;
+
+       del_timer(&dmxdevfilter->timer);
+       if (para->timeout) {
+               dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout;
+               dmxdevfilter->timer.data = (unsigned long)dmxdevfilter;
+               dmxdevfilter->timer.expires =
+                   jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;
+               add_timer(&dmxdevfilter->timer);
+       }
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+                                      const u8 *buffer2, size_t buffer2_len,
+                                      struct dmx_section_filter *filter)
+{
+       struct dmxdev_filter *dmxdevfilter = filter->priv;
+       int ret;
+
+       if (dmxdevfilter->buffer.error) {
+               wake_up(&dmxdevfilter->buffer.queue);
+               return 0;
+       }
+       spin_lock(&dmxdevfilter->dev->lock);
+       if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+               spin_unlock(&dmxdevfilter->dev->lock);
+               return 0;
+       }
+       del_timer(&dmxdevfilter->timer);
+#if 0
+       dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
+               buffer1[0], buffer1[1],
+               buffer1[2], buffer1[3], buffer1[4], buffer1[5]);
+#endif
+       ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
+                                     buffer1_len);
+       if (ret == buffer1_len) {
+               ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
+                                             buffer2_len);
+       }
+       if (ret < 0) {
+               dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+               dmxdevfilter->buffer.error = ret;
+       }
+       if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
+               dmxdevfilter->state = DMXDEV_STATE_DONE;
+       spin_unlock(&dmxdevfilter->dev->lock);
+       wake_up(&dmxdevfilter->buffer.queue);
+       return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+                                 const u8 *buffer2, size_t buffer2_len,
+                                 struct dmx_ts_feed *feed)
+{
+       struct dmxdev_filter *dmxdevfilter = feed->priv;
+       struct dvb_ringbuffer *buffer;
+       int ret;
+
+       spin_lock(&dmxdevfilter->dev->lock);
+       if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+               spin_unlock(&dmxdevfilter->dev->lock);
+               return 0;
+       }
+
+       if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+           || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+               buffer = &dmxdevfilter->buffer;
+       else
+               buffer = &dmxdevfilter->dev->dvr_buffer;
+       if (buffer->error) {
+               spin_unlock(&dmxdevfilter->dev->lock);
+               wake_up(&buffer->queue);
+               return 0;
+       }
+       ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+       if (ret == buffer1_len)
+               ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+       if (ret < 0) {
+               dvb_ringbuffer_flush(buffer);
+               buffer->error = ret;
+       }
+       spin_unlock(&dmxdevfilter->dev->lock);
+       wake_up(&buffer->queue);
+       return 0;
+}
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+       struct dmxdev_feed *feed;
+
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+       switch (dmxdevfilter->type) {
+       case DMXDEV_TYPE_SEC:
+               del_timer(&dmxdevfilter->timer);
+               dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+               break;
+       case DMXDEV_TYPE_PES:
+               list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+                       feed->ts->stop_filtering(feed->ts);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* start feed associated with the specified filter */
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+       struct dmxdev_feed *feed;
+       int ret;
+
+       dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+
+       switch (filter->type) {
+       case DMXDEV_TYPE_SEC:
+               return filter->feed.sec->start_filtering(filter->feed.sec);
+       case DMXDEV_TYPE_PES:
+               list_for_each_entry(feed, &filter->feed.ts, next) {
+                       ret = feed->ts->start_filtering(feed->ts);
+                       if (ret < 0) {
+                               dvb_dmxdev_feed_stop(filter);
+                               return ret;
+                       }
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/* restart section feed if it has filters left associated with it,
+   otherwise release the feed */
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+       int i;
+       struct dmxdev *dmxdev = filter->dev;
+       u16 pid = filter->params.sec.pid;
+
+       for (i = 0; i < dmxdev->filternum; i++)
+               if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+                   dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+                   dmxdev->filter[i].params.sec.pid == pid) {
+                       dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+                       return 0;
+               }
+
+       filter->dev->demux->release_section_feed(dmxdev->demux,
+                                                filter->feed.sec);
+
+       return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+       struct dmxdev_feed *feed;
+       struct dmx_demux *demux;
+
+       if (dmxdevfilter->state < DMXDEV_STATE_GO)
+               return 0;
+
+       switch (dmxdevfilter->type) {
+       case DMXDEV_TYPE_SEC:
+               if (!dmxdevfilter->feed.sec)
+                       break;
+               dvb_dmxdev_feed_stop(dmxdevfilter);
+               if (dmxdevfilter->filter.sec)
+                       dmxdevfilter->feed.sec->
+                           release_filter(dmxdevfilter->feed.sec,
+                                          dmxdevfilter->filter.sec);
+               dvb_dmxdev_feed_restart(dmxdevfilter);
+               dmxdevfilter->feed.sec = NULL;
+               break;
+       case DMXDEV_TYPE_PES:
+               dvb_dmxdev_feed_stop(dmxdevfilter);
+               demux = dmxdevfilter->dev->demux;
+               list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+                       demux->release_ts_feed(demux, feed->ts);
+                       feed->ts = NULL;
+               }
+               break;
+       default:
+               if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
+                       return 0;
+               return -EINVAL;
+       }
+
+       dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+       return 0;
+}
+
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+       struct dmxdev_feed *feed, *tmp;
+
+       /* delete all PIDs */
+       list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+               list_del(&feed->next);
+               kfree(feed);
+       }
+
+       BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+       if (dmxdevfilter->state < DMXDEV_STATE_SET)
+               return 0;
+
+       if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+               dvb_dmxdev_delete_pids(dmxdevfilter);
+
+       dmxdevfilter->type = DMXDEV_TYPE_NONE;
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+       return 0;
+}
+
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+                                struct dmxdev_filter *filter,
+                                struct dmxdev_feed *feed)
+{
+       struct timespec timeout = { 0 };
+       struct dmx_pes_filter_params *para = &filter->params.pes;
+       dmx_output_t otype;
+       int ret;
+       int ts_type;
+       dmx_pes_type_t ts_pes;
+       struct dmx_ts_feed *tsfeed;
+
+       feed->ts = NULL;
+       otype = para->output;
+
+       ts_pes = para->pes_type;
+
+       if (ts_pes < DMX_PES_OTHER)
+               ts_type = TS_DECODER;
+       else
+               ts_type = 0;
+
+       if (otype == DMX_OUT_TS_TAP)
+               ts_type |= TS_PACKET;
+       else if (otype == DMX_OUT_TSDEMUX_TAP)
+               ts_type |= TS_PACKET | TS_DEMUX;
+       else if (otype == DMX_OUT_TAP)
+               ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+       ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+                                             dvb_dmxdev_ts_callback);
+       if (ret < 0)
+               return ret;
+
+       tsfeed = feed->ts;
+       tsfeed->priv = filter;
+
+       ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+       if (ret < 0) {
+               dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+               return ret;
+       }
+
+       ret = tsfeed->start_filtering(tsfeed);
+       if (ret < 0) {
+               dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+       struct dmxdev *dmxdev = filter->dev;
+       struct dmxdev_feed *feed;
+       void *mem;
+       int ret, i;
+
+       if (filter->state < DMXDEV_STATE_SET)
+               return -EINVAL;
+
+       if (filter->state >= DMXDEV_STATE_GO)
+               dvb_dmxdev_filter_stop(filter);
+
+       if (!filter->buffer.data) {
+               mem = vmalloc(filter->buffer.size);
+               if (!mem)
+                       return -ENOMEM;
+               spin_lock_irq(&filter->dev->lock);
+               filter->buffer.data = mem;
+               spin_unlock_irq(&filter->dev->lock);
+       }
+
+       dvb_ringbuffer_flush(&filter->buffer);
+
+       switch (filter->type) {
+       case DMXDEV_TYPE_SEC:
+       {
+               struct dmx_sct_filter_params *para = &filter->params.sec;
+               struct dmx_section_filter **secfilter = &filter->filter.sec;
+               struct dmx_section_feed **secfeed = &filter->feed.sec;
+
+               *secfilter = NULL;
+               *secfeed = NULL;
+
+
+               /* find active filter/feed with same PID */
+               for (i = 0; i < dmxdev->filternum; i++) {
+                       if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+                           dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+                           dmxdev->filter[i].params.sec.pid == para->pid) {
+                               *secfeed = dmxdev->filter[i].feed.sec;
+                               break;
+                       }
+               }
+
+               /* if no feed found, try to allocate new one */
+               if (!*secfeed) {
+                       ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
+                                                                  secfeed,
+                                                                  dvb_dmxdev_section_callback);
+                       if (ret < 0) {
+                               printk("DVB (%s): could not alloc feed\n",
+                                      __func__);
+                               return ret;
+                       }
+
+                       ret = (*secfeed)->set(*secfeed, para->pid, 32768,
+                                             (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+                       if (ret < 0) {
+                               printk("DVB (%s): could not set feed\n",
+                                      __func__);
+                               dvb_dmxdev_feed_restart(filter);
+                               return ret;
+                       }
+               } else {
+                       dvb_dmxdev_feed_stop(filter);
+               }
+
+               ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
+               if (ret < 0) {
+                       dvb_dmxdev_feed_restart(filter);
+                       filter->feed.sec->start_filtering(*secfeed);
+                       dprintk("could not get filter\n");
+                       return ret;
+               }
+
+               (*secfilter)->priv = filter;
+
+               memcpy(&((*secfilter)->filter_value[3]),
+                      &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
+               memcpy(&(*secfilter)->filter_mask[3],
+                      &para->filter.mask[1], DMX_FILTER_SIZE - 1);
+               memcpy(&(*secfilter)->filter_mode[3],
+                      &para->filter.mode[1], DMX_FILTER_SIZE - 1);
+
+               (*secfilter)->filter_value[0] = para->filter.filter[0];
+               (*secfilter)->filter_mask[0] = para->filter.mask[0];
+               (*secfilter)->filter_mode[0] = para->filter.mode[0];
+               (*secfilter)->filter_mask[1] = 0;
+               (*secfilter)->filter_mask[2] = 0;
+
+               filter->todo = 0;
+
+               ret = filter->feed.sec->start_filtering(filter->feed.sec);
+               if (ret < 0)
+                       return ret;
+
+               dvb_dmxdev_filter_timer(filter);
+               break;
+       }
+       case DMXDEV_TYPE_PES:
+               list_for_each_entry(feed, &filter->feed.ts, next) {
+                       ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+                       if (ret < 0) {
+                               dvb_dmxdev_filter_stop(filter);
+                               return ret;
+                       }
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+       return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       int i;
+       struct dmxdev_filter *dmxdevfilter;
+
+       if (!dmxdev->filter)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&dmxdev->mutex))
+               return -ERESTARTSYS;
+
+       for (i = 0; i < dmxdev->filternum; i++)
+               if (dmxdev->filter[i].state == DMXDEV_STATE_FREE)
+                       break;
+
+       if (i == dmxdev->filternum) {
+               mutex_unlock(&dmxdev->mutex);
+               return -EMFILE;
+       }
+
+       dmxdevfilter = &dmxdev->filter[i];
+       mutex_init(&dmxdevfilter->mutex);
+       file->private_data = dmxdevfilter;
+
+       dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+       dmxdevfilter->type = DMXDEV_TYPE_NONE;
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+       init_timer(&dmxdevfilter->timer);
+
+       dvbdev->users++;
+
+       mutex_unlock(&dmxdev->mutex);
+       return 0;
+}
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
+                                 struct dmxdev_filter *dmxdevfilter)
+{
+       mutex_lock(&dmxdev->mutex);
+       mutex_lock(&dmxdevfilter->mutex);
+
+       dvb_dmxdev_filter_stop(dmxdevfilter);
+       dvb_dmxdev_filter_reset(dmxdevfilter);
+
+       if (dmxdevfilter->buffer.data) {
+               void *mem = dmxdevfilter->buffer.data;
+
+               spin_lock_irq(&dmxdev->lock);
+               dmxdevfilter->buffer.data = NULL;
+               spin_unlock_irq(&dmxdev->lock);
+               vfree(mem);
+       }
+
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+       wake_up(&dmxdevfilter->buffer.queue);
+       mutex_unlock(&dmxdevfilter->mutex);
+       mutex_unlock(&dmxdev->mutex);
+       return 0;
+}
+
+static inline void invert_mode(dmx_filter_t *filter)
+{
+       int i;
+
+       for (i = 0; i < DMX_FILTER_SIZE; i++)
+               filter->mode[i] ^= 0xff;
+}
+
+static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
+                             struct dmxdev_filter *filter, u16 pid)
+{
+       struct dmxdev_feed *feed;
+
+       if ((filter->type != DMXDEV_TYPE_PES) ||
+           (filter->state < DMXDEV_STATE_SET))
+               return -EINVAL;
+
+       /* only TS packet filters may have multiple PIDs */
+       if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+           (!list_empty(&filter->feed.ts)))
+               return -EINVAL;
+
+       feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+       if (feed == NULL)
+               return -ENOMEM;
+
+       feed->pid = pid;
+       list_add(&feed->next, &filter->feed.ts);
+
+       if (filter->state >= DMXDEV_STATE_GO)
+               return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+       return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+                                 struct dmxdev_filter *filter, u16 pid)
+{
+       struct dmxdev_feed *feed, *tmp;
+
+       if ((filter->type != DMXDEV_TYPE_PES) ||
+           (filter->state < DMXDEV_STATE_SET))
+               return -EINVAL;
+
+       list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+               if ((feed->pid == pid) && (feed->ts != NULL)) {
+                       feed->ts->stop_filtering(feed->ts);
+                       filter->dev->demux->release_ts_feed(filter->dev->demux,
+                                                           feed->ts);
+                       list_del(&feed->next);
+                       kfree(feed);
+               }
+       }
+
+       return 0;
+}
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+                                struct dmxdev_filter *dmxdevfilter,
+                                struct dmx_sct_filter_params *params)
+{
+       dprintk("function : %s\n", __func__);
+
+       dvb_dmxdev_filter_stop(dmxdevfilter);
+
+       dmxdevfilter->type = DMXDEV_TYPE_SEC;
+       memcpy(&dmxdevfilter->params.sec,
+              params, sizeof(struct dmx_sct_filter_params));
+       invert_mode(&dmxdevfilter->params.sec.filter);
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+       if (params->flags & DMX_IMMEDIATE_START)
+               return dvb_dmxdev_filter_start(dmxdevfilter);
+
+       return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+                                    struct dmxdev_filter *dmxdevfilter,
+                                    struct dmx_pes_filter_params *params)
+{
+       int ret;
+
+       dprintk("function : %s\n", __func__);
+
+       dvb_dmxdev_filter_stop(dmxdevfilter);
+       dvb_dmxdev_filter_reset(dmxdevfilter);
+
+       if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
+               return -EINVAL;
+
+       dmxdevfilter->type = DMXDEV_TYPE_PES;
+       memcpy(&dmxdevfilter->params, params,
+              sizeof(struct dmx_pes_filter_params));
+       INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
+
+       dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+       ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+                                dmxdevfilter->params.pes.pid);
+       if (ret < 0)
+               return ret;
+
+       if (params->flags & DMX_IMMEDIATE_START)
+               return dvb_dmxdev_filter_start(dmxdevfilter);
+
+       return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+                                  struct file *file, char __user *buf,
+                                  size_t count, loff_t *ppos)
+{
+       int result, hcount;
+       int done = 0;
+
+       if (dfil->todo <= 0) {
+               hcount = 3 + dfil->todo;
+               if (hcount > count)
+                       hcount = count;
+               result = dvb_dmxdev_buffer_read(&dfil->buffer,
+                                               file->f_flags & O_NONBLOCK,
+                                               buf, hcount, ppos);
+               if (result < 0) {
+                       dfil->todo = 0;
+                       return result;
+               }
+               if (copy_from_user(dfil->secheader - dfil->todo, buf, result))
+                       return -EFAULT;
+               buf += result;
+               done = result;
+               count -= result;
+               dfil->todo -= result;
+               if (dfil->todo > -3)
+                       return done;
+               dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff;
+               if (!count)
+                       return done;
+       }
+       if (count > dfil->todo)
+               count = dfil->todo;
+       result = dvb_dmxdev_buffer_read(&dfil->buffer,
+                                       file->f_flags & O_NONBLOCK,
+                                       buf, count, ppos);
+       if (result < 0)
+               return result;
+       dfil->todo -= result;
+       return (result + done);
+}
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count,
+              loff_t *ppos)
+{
+       struct dmxdev_filter *dmxdevfilter = file->private_data;
+       int ret;
+
+       if (mutex_lock_interruptible(&dmxdevfilter->mutex))
+               return -ERESTARTSYS;
+
+       if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
+               ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+       else
+               ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+                                            file->f_flags & O_NONBLOCK,
+                                            buf, count, ppos);
+
+       mutex_unlock(&dmxdevfilter->mutex);
+       return ret;
+}
+
+static int dvb_demux_do_ioctl(struct file *file,
+                             unsigned int cmd, void *parg)
+{
+       struct dmxdev_filter *dmxdevfilter = file->private_data;
+       struct dmxdev *dmxdev = dmxdevfilter->dev;
+       unsigned long arg = (unsigned long)parg;
+       int ret = 0;
+
+       if (mutex_lock_interruptible(&dmxdev->mutex))
+               return -ERESTARTSYS;
+
+       switch (cmd) {
+       case DMX_START:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ERESTARTSYS;
+               }
+               if (dmxdevfilter->state < DMXDEV_STATE_SET)
+                       ret = -EINVAL;
+               else
+                       ret = dvb_dmxdev_filter_start(dmxdevfilter);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_STOP:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ERESTARTSYS;
+               }
+               ret = dvb_dmxdev_filter_stop(dmxdevfilter);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_SET_FILTER:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ERESTARTSYS;
+               }
+               ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_SET_PES_FILTER:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ERESTARTSYS;
+               }
+               ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_SET_BUFFER_SIZE:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       mutex_unlock(&dmxdev->mutex);
+                       return -ERESTARTSYS;
+               }
+               ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_GET_PES_PIDS:
+               if (!dmxdev->demux->get_pes_pids) {
+                       ret = -EINVAL;
+                       break;
+               }
+               dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
+               break;
+
+       case DMX_GET_CAPS:
+               if (!dmxdev->demux->get_caps) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = dmxdev->demux->get_caps(dmxdev->demux, parg);
+               break;
+
+       case DMX_SET_SOURCE:
+               if (!dmxdev->demux->set_source) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+               break;
+
+       case DMX_GET_STC:
+               if (!dmxdev->demux->get_stc) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = dmxdev->demux->get_stc(dmxdev->demux,
+                                            ((struct dmx_stc *)parg)->num,
+                                            &((struct dmx_stc *)parg)->stc,
+                                            &((struct dmx_stc *)parg)->base);
+               break;
+
+       case DMX_ADD_PID:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       case DMX_REMOVE_PID:
+               if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+               mutex_unlock(&dmxdevfilter->mutex);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&dmxdev->mutex);
+       return ret;
+}
+
+static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
+                           unsigned long arg)
+{
+       return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
+{
+       struct dmxdev_filter *dmxdevfilter = file->private_data;
+       unsigned int mask = 0;
+
+       if (!dmxdevfilter)
+               return -EINVAL;
+
+       poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+       if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+           dmxdevfilter->state != DMXDEV_STATE_DONE &&
+           dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+               return 0;
+
+       if (dmxdevfilter->buffer.error)
+               mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+       if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
+               mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+       return mask;
+}
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+       struct dmxdev_filter *dmxdevfilter = file->private_data;
+       struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+       int ret;
+
+       ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+       mutex_lock(&dmxdev->mutex);
+       dmxdev->dvbdev->users--;
+       if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+               fops_put(file->f_op);
+               file->f_op = NULL;
+               mutex_unlock(&dmxdev->mutex);
+               wake_up(&dmxdev->dvbdev->wait_queue);
+       } else
+               mutex_unlock(&dmxdev->mutex);
+
+       return ret;
+}
+
+static const struct file_operations dvb_demux_fops = {
+       .owner = THIS_MODULE,
+       .read = dvb_demux_read,
+       .unlocked_ioctl = dvb_demux_ioctl,
+       .open = dvb_demux_open,
+       .release = dvb_demux_release,
+       .poll = dvb_demux_poll,
+       .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_demux = {
+       .priv = NULL,
+       .users = 1,
+       .writers = 1,
+       .fops = &dvb_demux_fops
+};
+
+static int dvb_dvr_do_ioctl(struct file *file,
+                           unsigned int cmd, void *parg)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       unsigned long arg = (unsigned long)parg;
+       int ret;
+
+       if (mutex_lock_interruptible(&dmxdev->mutex))
+               return -ERESTARTSYS;
+
+       switch (cmd) {
+       case DMX_SET_BUFFER_SIZE:
+               ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&dmxdev->mutex);
+       return ret;
+}
+
+static long dvb_dvr_ioctl(struct file *file,
+                        unsigned int cmd, unsigned long arg)
+{
+       return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct dmxdev *dmxdev = dvbdev->priv;
+       unsigned int mask = 0;
+
+       dprintk("function : %s\n", __func__);
+
+       poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+       if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+               if (dmxdev->dvr_buffer.error)
+                       mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+               if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
+                       mask |= (POLLIN | POLLRDNORM | POLLPRI);
+       } else
+               mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+       return mask;
+}
+
+static const struct file_operations dvb_dvr_fops = {
+       .owner = THIS_MODULE,
+       .read = dvb_dvr_read,
+       .write = dvb_dvr_write,
+       .unlocked_ioctl = dvb_dvr_ioctl,
+       .open = dvb_dvr_open,
+       .release = dvb_dvr_release,
+       .poll = dvb_dvr_poll,
+       .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_dvr = {
+       .priv = NULL,
+       .readers = 1,
+       .users = 1,
+       .fops = &dvb_dvr_fops
+};
+
+int marutuner_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+       int i;
+
+       if (dmxdev->demux->open(dmxdev->demux) < 0)
+               return -EUSERS;
+
+       dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter));
+       if (!dmxdev->filter)
+               return -ENOMEM;
+
+       mutex_init(&dmxdev->mutex);
+       spin_lock_init(&dmxdev->lock);
+       for (i = 0; i < dmxdev->filternum; i++) {
+               dmxdev->filter[i].dev = dmxdev;
+               dmxdev->filter[i].buffer.data = NULL;
+               dvb_dmxdev_filter_state_set(&dmxdev->filter[i],
+                                           DMXDEV_STATE_FREE);
+       }
+
+       dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
+                           DVB_DEVICE_DEMUX);
+       dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
+                           dmxdev, DVB_DEVICE_DVR);
+
+       dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+
+       return 0;
+}
+
+void marutuner_dmxdev_release(struct dmxdev *dmxdev)
+{
+       dmxdev->exit=1;
+       if (dmxdev->dvbdev->users > 1) {
+               wait_event(dmxdev->dvbdev->wait_queue,
+                               dmxdev->dvbdev->users==1);
+       }
+       if (dmxdev->dvr_dvbdev->users > 1) {
+               wait_event(dmxdev->dvr_dvbdev->wait_queue,
+                               dmxdev->dvr_dvbdev->users==1);
+       }
+
+       dvb_unregister_device(dmxdev->dvbdev);
+       dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+       vfree(dmxdev->filter);
+       dmxdev->filter = NULL;
+       dmxdev->demux->close(dmxdev->demux);
+}
diff --git a/drivers/maru/tv/maru_dtv_decoder.c b/drivers/maru/tv/maru_dtv_decoder.c
new file mode 100644 (file)
index 0000000..4f4bd69
--- /dev/null
@@ -0,0 +1,1297 @@
+/*
+ * Virtual DTV Decoder Device Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *  GunSoo Kim <gunsoo83.kim@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
+ *
+ */
+/**
+ @file maru_dtv_decoder.c
+ @brief Virtual DTV Decoder Driver file.
+*/
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include "maru_v4l2.h"
+
+#include <linux/dvb/audio.h>
+#include "../drivers/media/dvb-core/dvbdev.h"
+
+MODULE_DESCRIPTION("Virtual DTV Decoder Device Driver");
+MODULE_AUTHOR("Gunsoo Kim <gunsoo83.kim@samsung.com");
+MODULE_LICENSE("GPL2");
+
+#define MARU_DTV_DEC_MODULE_NAME       "dtv-decoder"
+
+#define MAX_CC_ENTRY   128
+#define CC_PKT_SIZE    128
+
+static int dtv_decoder_debug = 0;
+module_param(dtv_decoder_debug, int, 0644);
+MODULE_PARM_DESC(dtv_decoder_debug, "Turn on/off maru DTV decoder debugging (default:off).");
+
+#define DEBUG(fmt, ...) \
+       do { \
+               if (dtv_decoder_debug > 0) { \
+                       printk(KERN_INFO "[%s][%s](%d): " \
+                                       fmt, MARU_DTV_DEC_MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__); \
+               } \
+       } while (0)
+
+#define ERROR(fmt, ...) \
+       printk(KERN_ERR "[%s][%s](%d): " \
+                       fmt, MARU_DTV_DEC_MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
+
+#define INFO(fmt, ...) \
+       printk(KERN_INFO "[%s][%s](%d): " \
+                       fmt, MARU_DTV_DEC_MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
+
+
+/* For sdp platform */
+/* for golfp */
+#define V4L2_CTRL_CLASS_CODEC_GOLFP    (0x009e0000)
+
+#define V4L2_CID_MFC_SET_INFO_BASE                                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 1)
+#define V4L2_CID_MFC_GET_INFO_BASE                                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 2)
+#define V4L2_CID_MFC_SET_EXTRA_DATA                                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 3)
+#define V4L2_CID_SDPMFC_EVENT_CLOSED_CCAPTION_STATUS   (V4L2_CTRL_CLASS_CODEC_GOLFP + 4)
+#define V4L2_CID_SDPMFC_EVENT_STATUS_UNMUTE                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 5)
+#define V4L2_CID_SDPMFC_EVENT_STATUS_LOCK                              (V4L2_CTRL_CLASS_CODEC_GOLFP + 6)
+#define V4L2_CID_SDPMFC_USERDATA_FILTERING_START               (V4L2_CTRL_CLASS_CODEC_GOLFP + 7)
+#define V4L2_CID_SDPMFC_USERDATA_FILTERING_STOP                        (V4L2_CTRL_CLASS_CODEC_GOLFP + 8)
+#define V4L2_CID_SDPMFC_CURRENT_STATUS_INFO                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 9)
+#define V4L2_CID_SDPMFC_CURRENT_STATUS_PTS                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 10)
+#define V4L2_CID_SDPMFC_G_SIGNAL_INFO                                  (V4L2_CTRL_CLASS_CODEC_GOLFP + 20)
+#define V4L2_CID_SDPMFC_S_DIMENSION_INFO                               (V4L2_CTRL_CLASS_CODEC_GOLFP + 21)
+#define V4L2_CID_SDPMFC_S_TRICK                                                        (V4L2_CTRL_CLASS_CODEC_GOLFP + 22)
+#define V4L2_CID_SDPMFC_S_VIRTUAL_STOP                                 (V4L2_CTRL_CLASS_CODEC_GOLFP + 23)
+#define V4L2_CID_SDPMFC_S_VIRTUAL_START                                        (V4L2_CTRL_CLASS_CODEC_GOLFP + 24)
+#define V4L2_CID_SDPMFC_S_STEP_PAUSE                                   (V4L2_CTRL_CLASS_CODEC_GOLFP + 25)
+#define V4L2_CID_SDPMFC_EVENT_INFO                                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 30)
+#define V4L2_CID_SDPMFC_CURRENT_STATUS_FPA                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 31)
+#define V4L2_CID_SDPMFC_CURRENT_STATUS_AFD                             (V4L2_CTRL_CLASS_CODEC_GOLFP + 32)
+#define V4L2_CID_SDPMFC_ENC_BITRATE                                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 100)
+#define V4L2_CID_MFC_VIDEO_OUTPUT                                              (V4L2_CTRL_CLASS_CODEC_GOLFP + 200)
+#define V4L2_CID_MFC_VIDEO_OUTPUT_Y                                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 201)
+#define V4L2_CID_MFC_VIDEO_OUTPUT_U                                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 202)
+#define V4L2_CID_MFC_VIDEO_OUTPUT_V                                            (V4L2_CTRL_CLASS_CODEC_GOLFP + 203)
+
+/* for hawk */
+#define V4L2_CTRL_CLASS_CODEC_HAWK             (0x00a20000)
+/* Codec class controls */
+#define V4L2_CID_SET_PARSE_INFO                                (V4L2_CTRL_CLASS_CODEC_HAWK + 1)
+#define V4L2_CID_SET_EXTRA_DATA                                (V4L2_CTRL_CLASS_CODEC_HAWK + 3)
+#define V4L2_CID_GET_USERDATA_CC                       (V4L2_CTRL_CLASS_CODEC_HAWK + 4)
+/* REMIND : these ext. API is obsolate */
+//#define V4L2_CID_EVENT_STATUS_UNMUTE         (V4L2_CTRL_CLASS_CODEC_HAWK + 5)
+//#define V4L2_CID_EVENT_STATUS_LOCK           (V4L2_CTRL_CLASS_CODEC_HAWK + 6)
+//#define V4L2_CID_USERDATA_FILTERING_START    (V4L2_CTRL_CLASS_CODEC_HAWK + 7)
+//#define V4L2_CID_USERDATA_FILTERING_STOP     (V4L2_CTRL_CLASS_CODEC_HAWK + 8)
+#define V4L2_CID_GET_VIDEO_INFO                                (V4L2_CTRL_CLASS_CODEC_HAWK + 9)
+#define V4L2_CID_GET_PTS                                       (V4L2_CTRL_CLASS_CODEC_HAWK + 10)
+#define V4L2_CID_GET_DISPLAY_FRAME_INFO                (V4L2_CTRL_CLASS_CODEC_HAWK + 20)
+#define V4L2_CID_SET_DIMENSION                         (V4L2_CTRL_CLASS_CODEC_HAWK + 21)
+#define V4L2_CID_SET_TRICK                                     (V4L2_CTRL_CLASS_CODEC_HAWK + 22)
+/* REMIND : these ext. API is obsolate */
+//#define V4L2_CID_SET_VIRTUAL_STOP                    (V4L2_CTRL_CLASS_CODEC_HAWK + 23)
+//#define V4L2_CID_SET_VIRTUAL_START           (V4L2_CTRL_CLASS_CODEC_HAWK + 24)
+#define V4L2_CID_DECORDER_FLUSH                                (V4L2_CTRL_CLASS_CODEC_HAWK + 23)
+#define V4L2_CID_SET_STEP_PAUSE                                (V4L2_CTRL_CLASS_CODEC_HAWK + 25)
+/* REMIND : these ext. API is obsolate */
+//#define V4L2_CID_EVENT_INFO                          (V4L2_CTRL_CLASS_CODEC_HAWK + 30)
+#define V4L2_CID_GET_USERDATA_FPA                      (V4L2_CTRL_CLASS_CODEC_HAWK + 31)
+#define V4L2_CID_GET_USERDATA_AFD                      (V4L2_CTRL_CLASS_CODEC_HAWK + 32)
+/* for SDP_HEN control id */
+#define V4L2_CID_SET_BITRATE                           (V4L2_CTRL_CLASS_CODEC_HAWK + 100)
+
+/* events */
+#define V4L2_EVENT_VIDEOINFO           (V4L2_EVENT_PRIVATE_START + 1)
+#define V4L2_EVENT_UNMUTE                      (V4L2_EVENT_PRIVATE_START + 2)
+#define V4L2_EVENT_USERDATA_CC (V4L2_EVENT_PRIVATE_START + 3)
+#define V4L2_EVENT_CAPTURE_DONE        (V4L2_EVENT_PRIVATE_START + 4)
+#define V4L2_EVENT_LOCK                        (V4L2_EVENT_PRIVATE_START + 5)
+#define V4L2_EVENT_USERDATA_FPA        (V4L2_EVENT_PRIVATE_START + 6)
+#define V4L2_EVENT_NOT_SUPPORT (V4L2_EVENT_PRIVATE_START + 7)
+
+
+
+#define V4L2_CAP_VIDEO_M2M     0x00008000
+
+/*  MPEG-class control IDs specific to the Samsung SDPMFC driver as defined by V4L2 */
+#define V4L2_CID_SDPMFC_BASE   (V4L2_CTRL_CLASS_MPEG | 0x1200)
+
+#define V4L2_CID_SDPMFC_INPUT_SOURCE   (V4L2_CID_SDPMFC_BASE+1)
+#define V4L2_CID_SDPMFC_SECURE_SUBMIT  (V4L2_CID_SDPMFC_BASE+2)
+
+enum v4l2_sdpmfc_input_source {
+       V4L2_SDPMFC_INPUT_SOURCE_AIR    = 0,
+       V4L2_SDPMFC_INPUT_SOURCE_MEDIA  = 1,
+       V4L2_SDPMFC_INPUT_SOURCE_PVR    = 2,
+       V4L2_SDPMFC_INPUT_SOURCE_CLIP   = 3,
+};
+
+enum v4l2_userdata_type {
+       SDP_MFC_USERDATA_CC             = 0x01, /* Closed Caption Data(Only from Picture User Data)*/
+       SDP_MFC_USERDATA_AFD    = 0x02, /* Digital Active Format Descriptor */
+       SDP_MFC_USERDATA_FPA    = 0x04, /* Frame Packing Arrangement in H.264 SEI */
+};
+
+/* for golfp */
+struct v4l2_video_status
+{
+       unsigned int  width;
+       unsigned int height;
+       unsigned int field;
+       struct v4l2_fract fr;
+       unsigned int lock;              //0 : unlock 1:locked
+       unsigned int unmute;    //0: mute, 1:unmute
+       unsigned int status;
+       enum v4l2_aspectratio_type aspect_ratio;        //  1-1:1 , 2-4:3, 3-16:9, 4-2.21:1
+};
+/* for hawk */
+struct sdp_videoinfo {
+       unsigned int width;
+       unsigned int height;
+       unsigned int aspect_ratio;
+       unsigned int framerate;
+       unsigned int progressive;
+       unsigned int crop_hoffset;
+       unsigned int crop_voffset;
+       unsigned int stride;
+};
+
+
+struct v4l2_sdp_vbi_format {
+       __u32 data_type;                        // 0x1 : DTV_CC, 0x2 : DTV_AFD, 0x4 : DTV_FPA
+       __u32 picture_structure;        // for digital CC
+       __u32 top_field_first;          // for digital CC
+       __u32 temporal_reference;       // for digital CC
+       __u32 pts;                                      // for digital CC
+       __u32 vbi_line;                         // source VBI Line Number, for analog
+       __u32 valid;                            // 0 : invalid, 1:valid
+       __u32 payload_size;                     // byte
+       __u8  payload[128];
+       __u32 reserved[2];
+};
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* Define i/o and api values.  */
+enum dtv_decoder_io_cmd {
+       DTV_DECODER_CMD_GET_VERSION       = 0,
+       DTV_DECODER_CMD_SET_AUDIO_CODEC,
+       DTV_DECODER_CMD_SET_VIDEO_CODEC,
+       DTV_DECODER_CMD_AUDIO_PLAY,
+       DTV_DECODER_CMD_AUDIO_STOP,
+       DTV_DECODER_CMD_VIDEO_PLAY,
+       DTV_DECODER_CMD_VIDEO_STOP,
+       DTV_DECODER_CMD_GET_VIDMEM_ADDR,
+       DTV_DECODER_CMD_GET_AUDIO_CLOCK,
+       DTV_DECODER_CMD_ISR,
+       DTV_DECODER_CMD_VIDEO_SET_OUTPUT_Y,
+       DTV_DECODER_CMD_VIDEO_SET_OUTPUT_U,
+       DTV_DECODER_CMD_VIDEO_SET_OUTPUT_V,
+       DTV_DECODER_CMD_CC_INDEX,
+       DTV_DECODER_CMD_CC_COMPLETE,
+       DTV_DECODER_CMD_GET_DEVIDE_ID,
+       DTV_DECODER_CMD_GET_VIDEO_EVENT_STATE,
+};
+
+enum dtv_decoder_isr_reason {
+       TUNER_DECODER_ISR_EVENT         = 0x01,
+       TUNER_DECODER_ISR_CCDATA        = 0x02,
+};
+
+enum dtv_decoder_event_type{
+       TUNER_DECODER_VLOCK_EVENT       = 0x01,
+       TUNER_DECODER_VINFO_EVENT       = 0x02,
+       TUNER_DECODER_UNMUTE_EVENT      = 0x04,
+};
+
+
+struct maru_dtv_decoder_device {
+       struct pci_dev *pdev;
+
+       /* I/O and Memory Region */
+       unsigned int *ioaddr;
+       unsigned int *memaddr;
+
+       resource_size_t io_start;
+       resource_size_t io_size;
+       resource_size_t mem_start;
+       resource_size_t mem_size;
+
+       spinlock_t lock;
+
+       int version;
+
+       /* video decoder interface with v4l2 */
+       struct v4l2_device v4l2_dev;
+       struct video_device *vfd;
+       unsigned int video_refs;
+       unsigned int cc_data_count;
+       wait_queue_head_t video_waitqueue;
+
+       /* audio decoder interface with dvb */
+       struct dvb_adapter dvb_audio_adapter;
+       struct dvb_device *dvb_audio_main_dev;
+       struct dvb_device *dvb_audio_sub_dev;
+       unsigned int audio_in_use;
+       struct audio_status     audio_state;
+};
+
+static struct maru_dtv_decoder_device *maru_dtv_dec;
+static DEFINE_MUTEX(dev_mutex);
+
+static void push_video_event(struct video_device *vfd, unsigned int event_type)
+{
+       struct v4l2_event event;
+
+       INFO("push_video_event event_type=%d\n", event_type);
+       /* vlock event */
+       if (event_type & TUNER_DECODER_VLOCK_EVENT) {
+               INFO("push_video_event vlock\n");
+               memset(&event, 0, sizeof(event));
+               event.type = V4L2_EVENT_LOCK;
+               event.u.data[0] = 1;
+               v4l2_event_queue(vfd, &event);
+       }
+
+       /* video info event */
+       if (event_type & TUNER_DECODER_VINFO_EVENT) {
+               INFO("push_video_event vinfo\n");
+               memset(&event, 0, sizeof(event));
+               event.type = V4L2_EVENT_VIDEOINFO;
+               {
+                       struct sdp_videoinfo videoinfo;
+                       videoinfo.width = 1920;
+                       videoinfo.height = 1080;
+                       videoinfo.aspect_ratio = V4L2_ASPECTRATIO_TYPE_900_1600;
+                       videoinfo.framerate = 4; // 29970
+                       videoinfo.progressive = V4L2_FIELD_NONE;
+                       memcpy(event.u.data, &videoinfo, sizeof(struct sdp_videoinfo));
+               }
+               v4l2_event_queue(vfd, &event);
+       }
+
+       /* unmute event */
+       if (event_type & TUNER_DECODER_UNMUTE_EVENT) {
+               INFO("push_video_event unmute\n");
+               memset(&event, 0, sizeof(event));
+               event.type = V4L2_EVENT_UNMUTE;
+               event.u.data[0] = 1;
+               v4l2_event_queue(vfd, &event);
+       }
+}
+
+static irqreturn_t maru_dtv_dec_irq_handler(int irq, void *dev_id)
+{
+       struct maru_dtv_decoder_device *dev = NULL;
+       unsigned long flags = 0;
+       unsigned int val = 0;
+       unsigned int cc_count = 0;
+       unsigned int event_type = 0;
+       struct v4l2_event event;
+
+       dev = (struct maru_dtv_decoder_device *)dev_id;
+
+       val = readl(dev->ioaddr + DTV_DECODER_CMD_ISR);
+       if (!val) {
+               //DEBUG("This irq is not for this module\n");
+               return IRQ_NONE;
+       }
+
+       DEBUG("Wake up the poll wait in v4l2 interface.(%x)\n", val);
+       if (val & TUNER_DECODER_ISR_EVENT) {
+               /* lock, video info, unmute event */
+               event_type = (val & 0x000000F0) >> 4;
+               DEBUG("event type = 0x%x\n", event_type);
+
+               push_video_event(dev->vfd, event_type);
+       }
+
+       if (val & TUNER_DECODER_ISR_CCDATA) {
+               /* closed caption data */
+               cc_count = (val & 0xFFF00000) >> 20;
+               DEBUG("cc count = %d\n", cc_count);
+
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->cc_data_count = cc_count;
+               spin_unlock_irqrestore(&dev->lock, flags);
+
+               /* queue CC event */
+               memset(&event, 0, sizeof(event));
+               event.type = V4L2_EVENT_USERDATA_CC;
+               v4l2_event_queue(dev->vfd, &event);
+       }
+
+       if (cc_count > 0 || event_type > 0) {
+               wake_up_interruptible(&dev->video_waitqueue);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void maru_dtv_decoder_get_device_version(void)
+{
+       maru_dtv_dec->version =
+               readl(maru_dtv_dec->ioaddr + DTV_DECODER_CMD_GET_VERSION);
+
+       INFO("Device version: %d\n", maru_dtv_dec->version);
+}
+
+/* ------------------------------------------------------------------
+       video operations handler
+   ------------------------------------------------------------------*/
+/* v4l2 ioctl handler */
+static int vidioc_querycap(struct file *file, void *priv,
+                                               struct v4l2_capability *cap)
+{
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+       int ret = 0;
+
+       strcpy(cap->driver, MARU_DTV_DEC_MODULE_NAME);
+       strcpy(cap->card, MARU_DTV_DEC_MODULE_NAME);
+       strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+       cap->version = dev->version;
+       cap->capabilities =
+               V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_OUTPUT;
+
+       DEBUG("v4l2_capability capabilities=0x%x\n", cap->capabilities);
+       return ret;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+                                               enum v4l2_buf_type i)
+{
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+       int ret = 0;
+
+       DEBUG("v4l2_buf_type=%d\n", i);
+       writel((int32_t)i, dev->ioaddr + DTV_DECODER_CMD_VIDEO_PLAY);
+
+       return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+                                               enum v4l2_buf_type i)
+{
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+       int ret = 0;
+
+       DEBUG("v4l2_buf_type=%d\n", i);
+       writel((int32_t)i, dev->ioaddr + DTV_DECODER_CMD_VIDEO_STOP);
+
+       return ret;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+                                               struct v4l2_control *ctrl)
+{
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+       int ret = 0;
+
+       DEBUG("v4l2_control id=%x, value=%d\n", ctrl->id, ctrl->value);
+       switch (ctrl->id) {
+               case V4L2_CID_MFC_VIDEO_OUTPUT:
+                       //writel((u32)ctrl->value, dev->ioaddr + DTV_DECODER_CMD_VIDEO_SET_OUTPUT);
+                       break;
+
+               case V4L2_CID_MFC_VIDEO_OUTPUT_Y:
+                       writel((u32)ctrl->value, dev->ioaddr + DTV_DECODER_CMD_VIDEO_SET_OUTPUT_Y);
+                       break;
+
+               case V4L2_CID_MFC_VIDEO_OUTPUT_U:
+                       writel((u32)ctrl->value, dev->ioaddr + DTV_DECODER_CMD_VIDEO_SET_OUTPUT_U);
+                       break;
+
+               case V4L2_CID_MFC_VIDEO_OUTPUT_V:
+                       writel((u32)ctrl->value, dev->ioaddr + DTV_DECODER_CMD_VIDEO_SET_OUTPUT_V);
+                       break;
+
+               case V4L2_CID_SDPMFC_INPUT_SOURCE:
+                       DEBUG("V4L2_CID_SDPMFC_INPUT_SOURCE\n");
+                       break;
+       }
+       return ret;
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+                                               struct v4l2_format *f)
+{
+       int ret = 0;
+
+       DEBUG("v4l2_format type=%d, pixelformat=%d\n",
+                       f->type, f->fmt.pix.pixelformat);
+       return ret;
+}
+
+static int get_closed_caption_data(struct maru_dtv_decoder_device *dev,
+                                               struct v4l2_ext_control *ctrl)
+{
+       struct v4l2_sdp_vbi_format vbi_fmt;
+       struct v4l2_sdp_vbi_format *pvbi_fmt;
+       int cc_idx = 0;
+       int cc_data_count = 0;
+       uint32_t offset = 0;
+       unsigned long flags = 0;
+       int ret = 0;
+
+       //DEBUG("Get closed caption data.\n");
+
+       pvbi_fmt = (struct v4l2_sdp_vbi_format *)ctrl->string;
+
+       if (pvbi_fmt == NULL) {
+               ERROR("Invalid parameter.\n");
+               return -EINVAL;
+       }
+
+       cc_idx = readl(dev->ioaddr + DTV_DECODER_CMD_CC_INDEX);
+       if (cc_idx == MAX_CC_ENTRY) {
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->cc_data_count = 0;
+               spin_unlock_irqrestore(&dev->lock, flags);
+
+               memset(&vbi_fmt, 0, sizeof(struct v4l2_sdp_vbi_format));
+
+               if (copy_to_user(pvbi_fmt, &vbi_fmt, sizeof(vbi_fmt))) {
+                       ret = -EFAULT;
+               }
+               DEBUG("closed caption queue is empty.\n");
+               return ret;
+       }
+
+       offset = cc_idx * CC_PKT_SIZE;
+       DEBUG("cc_idx=%d, offset=%d, ref=%d\n", cc_idx, offset, cc_idx%15);
+
+       memset(&vbi_fmt, 0, sizeof(struct v4l2_sdp_vbi_format));
+
+       memcpy(&vbi_fmt.pts, (uint8_t*)dev->memaddr + offset, sizeof(vbi_fmt.pts));
+       offset += sizeof(vbi_fmt.pts);
+       memcpy(&vbi_fmt.payload_size, (uint8_t*)dev->memaddr + offset, sizeof(vbi_fmt.payload_size));
+       offset += sizeof(vbi_fmt.payload_size);
+       memcpy(vbi_fmt.payload, (uint8_t*)dev->memaddr + offset, vbi_fmt.payload_size);
+
+       vbi_fmt.temporal_reference = cc_idx%15;
+       vbi_fmt.data_type = SDP_MFC_USERDATA_CC;
+       vbi_fmt.picture_structure = 3;
+       vbi_fmt.top_field_first = 1;
+       vbi_fmt.vbi_line= 0;
+       vbi_fmt.valid = 1;
+
+
+       DEBUG("cc data pts=%u, size=%d.\n", vbi_fmt.pts, vbi_fmt.payload_size);
+       DEBUG("%x %x %x %x  %x %x %x %x\n",
+                       vbi_fmt.payload[0], vbi_fmt.payload[1], vbi_fmt.payload[2], vbi_fmt.payload[3],
+                       vbi_fmt.payload[4], vbi_fmt.payload[5], vbi_fmt.payload[6], vbi_fmt.payload[7]);
+
+       if (copy_to_user(pvbi_fmt, &vbi_fmt, sizeof(vbi_fmt))) {
+               ret = -EFAULT;
+
+               DEBUG("invalid buffer for v4l2_sdp_vbi_format.\n");
+               return ret;
+       }
+
+       cc_data_count = readl(dev->ioaddr + DTV_DECODER_CMD_CC_COMPLETE);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->cc_data_count = cc_data_count;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       DEBUG("data count=%d.\n", cc_data_count);
+
+       return ret;
+}
+
+static int get_video_event_info(struct maru_dtv_decoder_device *dev, struct v4l2_ext_control *ctrl)
+{
+       struct v4l2_video_status vstatus;
+       struct v4l2_video_status *pvstatus;
+       int ret = 0;
+       unsigned int event_state = 0;
+
+       event_state = readl(dev->ioaddr + DTV_DECODER_CMD_GET_VIDEO_EVENT_STATE);
+
+       DEBUG("Get event information. event_state=%x(lock, resolution, unmute)\n", event_state);
+
+       pvstatus = (struct v4l2_video_status *)ctrl->string;
+
+       /* lock infomation */
+       if (event_state & TUNER_DECODER_VLOCK_EVENT) {
+               vstatus.lock = 1;       //0: unlock, 1: locked
+               vstatus.status = vstatus.status | 0x1;
+       }
+
+       /* resolution information */
+       if (event_state & TUNER_DECODER_VINFO_EVENT) {
+               vstatus.width = 1920;
+               vstatus.height = 1080;
+               vstatus.field = V4L2_FIELD_NONE;
+               vstatus.fr.denominator = 29970;
+               vstatus.fr.numerator = 1;
+               vstatus.aspect_ratio = V4L2_ASPECTRATIO_TYPE_900_1600;
+               vstatus.status = vstatus.status | 0x10;
+       }
+
+       /* unmute infomation */
+       if (event_state & TUNER_DECODER_UNMUTE_EVENT) {
+               vstatus.unmute = 1;     //0: mute, 1: unmute
+               vstatus.status = vstatus.status | 0x100;
+       }
+
+       if (copy_to_user(pvstatus, &vstatus, sizeof(vstatus))) {
+               ret = -EFAULT;
+       }
+
+       return ret;
+}
+
+static int get_display_frame_info(struct v4l2_ext_control *ctrl)
+{
+       struct v4l2_drm drm;
+       struct v4l2_drm *pdrm;
+       int ret = 0;
+
+       DEBUG("Get DRM information.\n");
+
+       pdrm = (struct v4l2_drm *)ctrl->string;
+
+       /* TODO: set proper value */
+       drm.inputport = V4L2_DRM_INPUTPORT_TYPE_DTV;
+
+       drm.u.dec_info.framerate = 2997;
+       drm.u.dec_info.aspectratio = V4L2_ASPECTRATIO_TYPE_900_1600;
+       drm.u.dec_info.hres = 1920;
+       drm.u.dec_info.vres = 1080;
+       drm.u.dec_info.scantype = 0;
+       drm.u.dec_info.pFrame[0].showframe = 1;
+
+       drm.u.dec_info.pFrame[0].y_linesize = 1920;
+       drm.u.dec_info.pFrame[0].u_linesize = 1920/2;
+       drm.u.dec_info.pFrame[0].v_linesize = 1920/2;
+
+       drm.reserved = 0x1234ABCD;
+
+       if (copy_to_user(pdrm, &drm, sizeof(drm))) {
+               ret = -EFAULT;
+       }
+
+       return ret;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *pfh,
+                                                       struct v4l2_ext_controls *ctrls)
+{
+       struct v4l2_ext_control *ctrl;
+       struct v4l2_sdp_vbi_format *pvbi_fmt;
+       struct v4l2_sdp_vbi_format vbi_fmt;
+       unsigned long flags = 0;
+       int ret = 0;
+       int cc_data_count = 0;
+       int i;
+       struct maru_dtv_decoder_device *dev = NULL;
+       struct v4l2_fh *fh = (struct v4l2_fh*)pfh;
+       dev = dev_get_drvdata(&fh->vdev->dev);
+
+       if (ctrls->ctrl_class != V4L2_CTRL_CLASS_CODEC_GOLFP &&
+               ctrls->ctrl_class != V4L2_CTRL_CLASS_CODEC_HAWK) {
+               DEBUG("Not supported class : %d\n", ctrls->ctrl_class);
+               ret = -EINVAL;
+               return ret;
+       }
+
+       DEBUG("ctrl class=%x. (golfp=9e0000,hawk=a20000)\n", ctrls->ctrl_class);
+
+       for (i=0; i<ctrls->count; i++)
+       {
+               ctrl = ctrls->controls + i;
+               DEBUG("v4l2_ext_controls ctrl->id=%d\n", ctrl->id);
+               switch (ctrl->id) {
+                       case V4L2_CID_SDPMFC_EVENT_INFO:
+                       case V4L2_CID_GET_VIDEO_INFO:
+                               /* TODO: read event_info from decoder device. */
+                               ret = get_video_event_info(dev, ctrl);
+                               break;
+
+                       case V4L2_CID_SDPMFC_G_SIGNAL_INFO:
+                       case V4L2_CID_GET_DISPLAY_FRAME_INFO:
+                               ret = get_display_frame_info(ctrl);
+                               break;
+
+                       case V4L2_CID_SDPMFC_CURRENT_STATUS_FPA:
+                       case V4L2_CID_GET_USERDATA_FPA:
+                               pvbi_fmt = (struct v4l2_sdp_vbi_format *)ctrl->string;
+
+                               memset(&vbi_fmt, 0, sizeof(struct v4l2_sdp_vbi_format));
+                               vbi_fmt.data_type = SDP_MFC_USERDATA_FPA;
+
+                               if (copy_to_user(pvbi_fmt, &vbi_fmt, sizeof(vbi_fmt))) {
+                                       ret = -EFAULT;
+                               }
+                               DEBUG("Get FPA data\n");
+                               break;
+
+                       case V4L2_CID_SDPMFC_CURRENT_STATUS_AFD:
+                       case V4L2_CID_GET_USERDATA_AFD:
+                               pvbi_fmt = (struct v4l2_sdp_vbi_format *)ctrl->string;
+
+                               memset(&vbi_fmt, 0, sizeof(struct v4l2_sdp_vbi_format));
+                               vbi_fmt.data_type = SDP_MFC_USERDATA_AFD;
+
+                               if (copy_to_user(pvbi_fmt, &vbi_fmt, sizeof(vbi_fmt))) {
+                                       ret = -EFAULT;
+                               }
+                               DEBUG("Get AFD data\n");
+                               break;
+
+                       case V4L2_CID_SDPMFC_EVENT_CLOSED_CCAPTION_STATUS:
+                       case V4L2_CID_GET_USERDATA_CC:
+                               spin_lock_irqsave(&dev->lock, flags);
+                               cc_data_count = dev->cc_data_count;
+                               spin_unlock_irqrestore(&dev->lock, flags);
+
+                               if (cc_data_count > 0) {
+                                       ret = get_closed_caption_data(dev, ctrl);
+                               }
+                               break;
+
+                       default:
+                               DEBUG("Not supported id: %d\n", ctrl->id);
+                               ret = -EINVAL;
+                               break;
+               }
+       }
+
+       return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *fh,
+                                                       struct v4l2_ext_controls *ctrls)
+{
+       struct v4l2_ext_control *ctrl;
+       int ret = 0;
+       int i;
+
+       DEBUG("v4l2_ext_controls ctrl_class=%d, count=%d\n",
+                       ctrls->ctrl_class, ctrls->count);
+
+       for (i=0; i<ctrls->count; i++)
+       {
+               ctrl = ctrls->controls + i;
+               DEBUG("control id=%d\n", ctrl->id);
+       }
+
+       return ret;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+                               const struct  v4l2_event_subscription *sub)
+{
+       switch (sub->type) {
+       case V4L2_EVENT_VIDEOINFO:                      /* DTV VIDEO INFO EVENT*/
+       case V4L2_EVENT_UNMUTE:                         /* DTV UNMUTE EVENT*/
+       case V4L2_EVENT_USERDATA_CC:
+       case V4L2_EVENT_CAPTURE_DONE:           /* DTV CAPTURE DONE EVENT*/
+       case V4L2_EVENT_LOCK:                           /* DTV LOCK EVENT*/
+       case V4L2_EVENT_USERDATA_FPA:
+       case V4L2_EVENT_NOT_SUPPORT:
+               return v4l2_event_subscribe(fh, sub, 1, NULL);
+       default:
+               return -EINVAL;
+       }
+}
+
+/* v4l2 file operation handler */
+static int maru_dtv_dec_video_open(struct file *file)
+{
+       int ret = 0;
+       struct v4l2_fh *fh = NULL;
+       struct v4l2_event_subscription sub;
+       struct maru_dtv_decoder_device *dev = video_drvdata(file);
+
+       DEBUG("opened dtv_dec_video device file\n");
+
+       fh = kzalloc(sizeof(struct v4l2_fh), GFP_KERNEL);
+       if (!fh) {
+               ERROR("Failed to allocate memory for v4l2_fh.\n");
+               return -ENOMEM;
+       }
+       v4l2_fh_init(fh, dev->vfd);
+       file->private_data = fh;
+       v4l2_fh_add(fh);
+
+       /* subscribe event for golfp */
+       memset(&sub, 0, sizeof (struct v4l2_event_subscription));
+       sub.type = V4L2_EVENT_LOCK;
+       v4l2_event_subscribe(fh, &sub, 1, NULL);
+
+       sub.type = V4L2_EVENT_VIDEOINFO;
+       v4l2_event_subscribe(fh, &sub, 1, NULL);
+
+       sub.type = V4L2_EVENT_UNMUTE;
+       v4l2_event_subscribe(fh, &sub, 1, NULL);
+
+       sub.type = V4L2_EVENT_USERDATA_CC;
+       v4l2_event_subscribe(fh, &sub, 1, NULL);
+
+       sub.type = V4L2_EVENT_USERDATA_FPA;
+       v4l2_event_subscribe(fh, &sub, 1, NULL);
+
+       mutex_lock(&dev_mutex);
+       if (dev->video_refs) {
+               dev->video_refs++;
+               DEBUG("video_refs=%d\n", dev->video_refs);
+               mutex_unlock(&dev_mutex);
+               return 0;
+       }
+
+       ret = request_irq(dev->pdev->irq, maru_dtv_dec_irq_handler,
+                               IRQF_SHARED, MARU_DTV_DEC_MODULE_NAME, dev);
+       if (ret) {
+               ERROR("Failed to request the irq: irq(#%d)\n",
+                               dev->pdev->irq);
+               mutex_unlock(&dev_mutex);
+               return ret;
+       }
+
+       dev->video_refs = 1;
+       mutex_unlock(&dev_mutex);
+
+       DEBUG("video_refs=%d\n", dev->video_refs);
+
+       return 0;
+}
+
+static int maru_dtv_dec_video_close(struct file *file)
+{
+       int ret = 0;
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+
+       DEBUG("closed dtv_dec_video device file\n");
+
+       mutex_lock(&dev_mutex);
+       dev->video_refs--;
+       if (dev->video_refs == 0) {
+               free_irq(dev->pdev->irq, dev);
+       }
+       mutex_unlock(&dev_mutex);
+
+       v4l2_fh_del(fh);
+       v4l2_fh_exit(fh);
+       kfree(fh);
+
+       DEBUG("video_refs=%d\n", dev->video_refs);
+
+       return ret;
+}
+
+static unsigned int maru_dtv_dec_video_poll(struct file *file,
+                                                                                struct poll_table_struct *wait)
+{
+       unsigned int ret = 0;
+       struct v4l2_fh *fh = file->private_data;
+       struct maru_dtv_decoder_device *dev = dev_get_drvdata(&fh->vdev->dev);
+
+       poll_wait(file, &dev->video_waitqueue, wait);
+
+       if (v4l2_event_pending(fh)) {
+               ret |= POLLPRI;
+       }
+
+       //TODO: In golfp, CC event is set POLLIN.
+       //DEBUG("ret=%d\n", ret);
+       return ret;
+}
+
+/* ------------------------------------------------------------------
+       audio operations handler
+   ------------------------------------------------------------------*/
+/* dvb ioctl handler */
+static int maru_dtv_dec_audio_ioctl(struct file *file,
+                                                               unsigned int cmd, void *parg)
+{
+       int ret = 0;
+       struct dvb_device *dvbdev = NULL;
+       struct maru_dtv_decoder_device *dev = NULL;
+       unsigned long arg = 0;
+
+       dvbdev = (struct dvb_device *)file->private_data;
+       dev = (struct maru_dtv_decoder_device *)dvbdev->priv;
+       arg = (unsigned long) parg;
+
+       switch (cmd) {
+               case AUDIO_STOP:
+                       dev->audio_state.play_state = AUDIO_STOPPED;
+                       writel((int32_t)AUDIO_STOPPED,
+                                       dev->ioaddr + DTV_DECODER_CMD_AUDIO_STOP);
+                       DEBUG("AUDIO_STOP : Stop dtv audio decoder\n");
+                       break;
+
+               case AUDIO_PLAY:
+                       dev->audio_state.play_state = AUDIO_PLAYING;
+                       writel((int32_t)AUDIO_PLAYING,
+                                       dev->ioaddr + DTV_DECODER_CMD_AUDIO_PLAY);
+                       DEBUG("AUDIO_PLAY : Start dtv audio decoder\n");
+                       break;
+
+               case AUDIO_PAUSE:
+                       dev->audio_state.play_state = AUDIO_PAUSED;
+                       DEBUG("AUDIO_PAUSE : Pause dtv audio decoder\n");
+                       break;
+
+               case AUDIO_SELECT_SOURCE:
+                       dev->audio_state.stream_source = (audio_stream_source_t) arg;
+                       DEBUG("AUDIO_SELECT_SOURCE : Select stream source=%d\n",
+                                       dev->audio_state.stream_source);
+                       break;
+
+               case AUDIO_SET_MUTE:
+                       dev->audio_state.mute_state = (int) arg;
+                       DEBUG("AUDIO_SET_MUTE : Set mute state=%d\n",
+                                       dev->audio_state.mute_state);
+                       break;
+
+               case AUDIO_SET_AV_SYNC:
+                       dev->audio_state.AV_sync_state = (int) arg;
+                       DEBUG("AUDIO_SET_AV_SYNC : Set AV_sync state=%d\n",
+                                       dev->audio_state.AV_sync_state);
+                       break;
+
+               case AUDIO_CHANNEL_SELECT:
+                       dev->audio_state.channel_select = (audio_channel_select_t) arg;
+                       DEBUG("AUDIO_CHANNEL_SELECT : Select channel=%d\n",
+                                       dev->audio_state.channel_select);
+                       break;
+
+               case AUDIO_SET_STREAMTYPE:
+                       writel((int32_t)arg,
+                                       dev->ioaddr + DTV_DECODER_CMD_SET_AUDIO_CODEC);
+                       DEBUG("AUDIO_SET_STREAMTYPE : Set audio decoder type=%d\n",
+                                       (int)arg);
+                       break;
+
+               default:
+                       DEBUG("not supported cmd: %d\n", cmd);
+                       ret = -ENOIOCTLCMD;
+                       break;
+       }
+
+       return ret;
+}
+
+/* dvb file operation handler */
+static int maru_dtv_dec_audio_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+       struct dvb_device *dvbdev = NULL;
+       struct maru_dtv_decoder_device *dev = NULL;
+
+       DEBUG("opened dtv_dec_audio device file\n");
+
+       dvbdev = (struct dvb_device *)file->private_data;
+       dev = (struct maru_dtv_decoder_device *)dvbdev->priv;
+
+       ret = dvb_generic_open(inode, file);
+       if (ret < 0) {
+               ERROR("dvb_generic_open failed.\n");
+               return ret;
+       }
+
+       mutex_lock(&dev_mutex);
+       if (dev->audio_in_use) {
+               ERROR("The device has been already opened.\n");
+               mutex_unlock(&dev_mutex);
+               return -EBUSY;
+       }
+
+       dev->audio_in_use = 1;
+       mutex_unlock(&dev_mutex);
+
+       DEBUG("audio_in_use=%d\n", dev->audio_in_use);
+
+       return ret;
+}
+
+static int maru_dtv_dec_audio_close(struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = NULL;
+       struct maru_dtv_decoder_device *dev = NULL;
+
+       DEBUG("closed dtv_dec_audio device file\n");
+
+       dvbdev = (struct dvb_device *)file->private_data;
+       dev = (struct maru_dtv_decoder_device *)dvbdev->priv;
+
+       mutex_lock(&dev_mutex);
+       dev->audio_in_use = 0;
+       mutex_unlock(&dev_mutex);
+
+       DEBUG("audio_in_use=%d\n", dev->audio_in_use);
+
+       return dvb_generic_release(inode, file);
+}
+
+/* dummy dvb handlers */
+static int maru_dtv_dec_audio_sub_ioctl(struct file *file,
+                                                               unsigned int cmd, void *parg)
+{
+       int ret = 0;
+
+       DEBUG("enter\n");
+       return ret;
+}
+
+static int maru_dtv_dec_audio_sub_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+
+       DEBUG("enter\n");
+       return ret;
+}
+
+static int maru_dtv_dec_audio_sub_close(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+
+       DEBUG("enter\n");
+       return ret;
+}
+
+static const struct v4l2_ioctl_ops maru_dtv_dec_video_ioctl_ops = {
+       .vidioc_querycap                = vidioc_querycap,
+       .vidioc_streamon                = vidioc_streamon,
+       .vidioc_streamoff               = vidioc_streamoff,
+       .vidioc_s_ctrl                  = vidioc_s_ctrl,
+       .vidioc_s_fmt_vid_out   = vidioc_s_fmt_vid_out,
+       .vidioc_g_ext_ctrls             = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls             = vidioc_s_ext_ctrls,
+       .vidioc_subscribe_event = vidioc_subscribe_event,
+       .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations maru_dtv_dec_video_fops = {
+       .owner          = THIS_MODULE,
+       .open           = maru_dtv_dec_video_open,
+       .release        = maru_dtv_dec_video_close,
+       .poll           = maru_dtv_dec_video_poll,
+       .mmap           = NULL,
+       .unlocked_ioctl         = video_ioctl2,
+};
+
+static struct video_device maru_dtv_dec_video_dev = {
+       .name                   = MARU_DTV_DEC_MODULE_NAME,
+       .fops                   = &maru_dtv_dec_video_fops,
+       .ioctl_ops              = &maru_dtv_dec_video_ioctl_ops,
+       .minor                  = -1,
+       .release                = video_device_release,
+};
+
+static const struct file_operations maru_dtv_dec_audio_fops = {
+       .owner                  = THIS_MODULE,
+       .unlocked_ioctl = dvb_generic_ioctl,
+       .open                   = maru_dtv_dec_audio_open,
+       .release                = maru_dtv_dec_audio_close,
+};
+
+static struct dvb_device maru_dtv_dec_audio_main_dev = {
+       .priv                   = NULL,
+       .users                  = 1,
+       .writers                = 1,
+       .fops                   = &maru_dtv_dec_audio_fops,
+       .kernel_ioctl   = maru_dtv_dec_audio_ioctl,
+};
+
+static const struct file_operations maru_dtv_dec_audio_sub_fops = {
+       .owner                  = THIS_MODULE,
+       .unlocked_ioctl = dvb_generic_ioctl,
+       .open                   = maru_dtv_dec_audio_sub_open,
+       .release                = maru_dtv_dec_audio_sub_close,
+};
+
+static struct dvb_device maru_dtv_dec_audio_sub_dev = {
+       .priv                   = NULL,
+       .users                  = 1,
+       .writers                = 1,
+       .fops                   = &maru_dtv_dec_audio_sub_fops,
+       .kernel_ioctl   = maru_dtv_dec_audio_sub_ioctl,
+};
+
+static int maru_dtv_decoder_probe(struct pci_dev *pci_dev,
+                                                                               const struct pci_device_id *pci_id)
+{
+       int ret = 0;
+
+       INFO("%s: driver is probed.\n", MARU_DTV_DEC_MODULE_NAME);
+
+       maru_dtv_dec = kzalloc(sizeof(struct maru_dtv_decoder_device), GFP_KERNEL);
+       if (!maru_dtv_dec) {
+               ERROR("Failed to allocate memory for codec.\n");
+               return -ENOMEM;
+       }
+
+       maru_dtv_dec->pdev = pci_dev;
+
+       spin_lock_init(&maru_dtv_dec->lock);
+
+       /* Initialize video decoder interface with v4l2 */
+       init_waitqueue_head(&maru_dtv_dec->video_waitqueue);
+       maru_dtv_dec->cc_data_count = 0;
+
+       ret = v4l2_device_register(&maru_dtv_dec->pdev->dev, &maru_dtv_dec->v4l2_dev);
+       if (ret) {
+               ERROR("v4l2_device_register failed!!\n");
+               goto err_v4l2_dev_reg;
+       }
+
+       ret = -ENOMEM;
+       maru_dtv_dec->vfd = video_device_alloc();
+       if (!maru_dtv_dec->vfd) {
+               ERROR("video_device_alloc() failed!!\n");
+               goto err_video_dec_alloc;
+       }
+
+       memcpy(maru_dtv_dec->vfd, &maru_dtv_dec_video_dev, sizeof(maru_dtv_dec_video_dev));
+
+       maru_dtv_dec->vfd->dev_parent = &maru_dtv_dec->pdev->dev;
+       maru_dtv_dec->vfd->v4l2_dev = &maru_dtv_dec->v4l2_dev;
+       maru_dtv_dec->vfd->vfl_dir = VFL_DIR_M2M;
+
+       ret = video_register_device(maru_dtv_dec->vfd, VFL_TYPE_GRABBER, 10);
+       if (ret < 0) {
+               ERROR("video_register_device failed\n");
+               goto err_video_dec_reg;
+       }
+
+       video_set_drvdata(maru_dtv_dec->vfd, maru_dtv_dec);
+
+       /* Initialize audio decoder interface with dvb */
+       adapter_nr[0] = 1; // for emulation to target hw
+       ret = dvb_register_adapter(&maru_dtv_dec->dvb_audio_adapter, "maru_dtv_dec_audio",
+                       THIS_MODULE, &maru_dtv_dec->pdev->dev, adapter_nr);
+       if (ret < 0) {
+               ERROR("dvb_register_adapter failed\n");
+               goto err_audio_adapter_reg;
+       }
+
+       /* main : dvb/adapter1/audio0
+               sub : dvb/adapter1/audio1 */
+       ret = dvb_register_device(&maru_dtv_dec->dvb_audio_adapter, &maru_dtv_dec->dvb_audio_main_dev,
+                       &maru_dtv_dec_audio_main_dev, maru_dtv_dec, DVB_DEVICE_AUDIO);
+       if (ret < 0) {
+               ERROR("main audio : dvb_register_device failed\n");
+               goto err_audio_main_dev_reg;
+       }
+       ret = dvb_register_device(&maru_dtv_dec->dvb_audio_adapter, &maru_dtv_dec->dvb_audio_sub_dev,
+                       &maru_dtv_dec_audio_sub_dev, maru_dtv_dec, DVB_DEVICE_AUDIO);
+       if (ret < 0) {
+               ERROR("sub audio : dvb_register_device failed\n");
+               goto err_audio_sub_dev_reg;
+       }
+
+       if ((ret = pci_enable_device(pci_dev))) {
+               ERROR("pci_enable_device failed\n");
+               goto err_pci_enable;
+       }
+       pci_set_master(pci_dev);
+
+       maru_dtv_dec->mem_start = pci_resource_start(pci_dev, 0);
+       maru_dtv_dec->mem_size = pci_resource_len(pci_dev, 0);
+       if (!maru_dtv_dec->mem_start) {
+               ERROR("data : pci_resource_start failed\n");
+               ret = -ENODEV;
+               goto err_pci_resource_data;
+       }
+
+       if (!request_mem_region(maru_dtv_dec->mem_start,
+                               maru_dtv_dec->mem_size,
+                               MARU_DTV_DEC_MODULE_NAME)) {
+               ERROR("data : request_mem_region failed\n");
+               ret = -EINVAL;
+               goto err_pci_resource_data;
+       }
+
+       maru_dtv_dec->memaddr =
+               ioremap_nocache(maru_dtv_dec->mem_start, maru_dtv_dec->mem_size);
+       if (!maru_dtv_dec->memaddr) {
+               ERROR("data : ioremap failed\n");
+               ret = -EINVAL;
+               goto err_pci_ioremap_data;
+       }
+
+       maru_dtv_dec->io_start = pci_resource_start(pci_dev, 1);
+       maru_dtv_dec->io_size = pci_resource_len(pci_dev, 1);
+       if (!maru_dtv_dec->io_start) {
+               ERROR("io : pci_resource_start failed\n");
+               ret = -ENODEV;
+               goto err_pci_resource_io;
+       }
+
+       if (!request_mem_region(maru_dtv_dec->io_start,
+                               maru_dtv_dec->io_size,
+                               MARU_DTV_DEC_MODULE_NAME)) {
+               ERROR("io : request_io_region failed\n");
+               ret = -EINVAL;
+               goto err_pci_resource_io;
+       }
+
+       maru_dtv_dec->ioaddr =
+               ioremap_nocache(maru_dtv_dec->io_start, maru_dtv_dec->io_size);
+       if (!maru_dtv_dec->ioaddr) {
+               ERROR("io : ioremap failed\n");
+               ret = -EINVAL;
+               goto err_pci_ioremap_io;
+       }
+
+       maru_dtv_decoder_get_device_version();
+
+       return 0;
+
+err_pci_ioremap_io:
+       /* release io memory region */
+       release_mem_region(maru_dtv_dec->io_start, maru_dtv_dec->io_size);
+err_pci_resource_io:
+err_pci_ioremap_data:
+       /* release data memory region */
+       release_mem_region(maru_dtv_dec->mem_start, maru_dtv_dec->mem_size);
+err_pci_resource_data:
+       /* disable pci device */
+       pci_disable_device(pci_dev);
+err_pci_enable:
+       /* unregister dvb device */
+       dvb_unregister_device(maru_dtv_dec->dvb_audio_sub_dev);
+err_audio_sub_dev_reg:
+       dvb_unregister_device(maru_dtv_dec->dvb_audio_main_dev);
+err_audio_main_dev_reg:
+       /* unregister dvb adapter */
+       dvb_unregister_adapter(&maru_dtv_dec->dvb_audio_adapter);
+err_audio_adapter_reg:
+       /* unregister video device */
+       video_unregister_device(maru_dtv_dec->vfd);
+err_video_dec_reg:
+       /* release video device */
+       video_device_release(maru_dtv_dec->vfd);
+err_video_dec_alloc:
+       /* unregister v4l2 device*/
+       v4l2_device_unregister(&maru_dtv_dec->v4l2_dev);
+err_v4l2_dev_reg:
+       /* release driver state structure */
+       kfree(maru_dtv_dec);
+       maru_dtv_dec = NULL;
+
+       return ret;
+}
+
+static void maru_dtv_decoder_remove(struct pci_dev *pci_dev)
+{
+       if (maru_dtv_dec) {
+
+               /* unregister video device */
+               video_unregister_device(maru_dtv_dec->vfd);
+
+               /* unregister dvb device */
+               dvb_unregister_device(maru_dtv_dec->dvb_audio_main_dev);
+               dvb_unregister_device(maru_dtv_dec->dvb_audio_sub_dev);
+
+               if (maru_dtv_dec->ioaddr) {
+                       iounmap(maru_dtv_dec->ioaddr);
+                       maru_dtv_dec->ioaddr = NULL;
+               }
+
+               if (maru_dtv_dec->memaddr) {
+                       iounmap(maru_dtv_dec->memaddr);
+                       maru_dtv_dec->memaddr = NULL;
+               }
+
+               if (maru_dtv_dec->io_start) {
+                       release_mem_region(maru_dtv_dec->io_start,
+                                       maru_dtv_dec->io_size);
+                       maru_dtv_dec->io_start = 0;
+               }
+
+               if (maru_dtv_dec->mem_start) {
+                       release_mem_region(maru_dtv_dec->mem_start,
+                                       maru_dtv_dec->mem_size);
+                       maru_dtv_dec->mem_start = 0;
+               }
+
+               /* unregister v4l2 device*/
+               v4l2_device_unregister(&maru_dtv_dec->v4l2_dev);
+
+               /* unregister dvb adapter */
+               dvb_unregister_adapter(&maru_dtv_dec->dvb_audio_adapter);
+
+               kfree(maru_dtv_dec);
+       }
+
+       pci_disable_device(pci_dev);
+}
+
+static struct pci_device_id maru_dtv_decoder_pci_table[] = {
+       {
+               .vendor         = PCI_VENDOR_ID_TIZEN,
+               .device         = PCI_DEVICE_ID_VIRTUAL_DTV_DECODER,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(pci, maru_dtv_decoder_pci_table);
+
+static struct pci_driver maru_dtv_decoder_driver = {
+       .name           = MARU_DTV_DEC_MODULE_NAME,
+       .id_table       = maru_dtv_decoder_pci_table,
+       .probe          = maru_dtv_decoder_probe,
+       .remove         = maru_dtv_decoder_remove,
+};
+
+static int __init maru_dtv_decoder_init(void)
+{
+       INFO("driver is initialized.\n");
+
+       return pci_register_driver(&maru_dtv_decoder_driver);
+}
+
+static void __exit maru_tuner_decoder_exit(void)
+{
+       INFO("driver is finalized.\n");
+
+       pci_unregister_driver(&maru_dtv_decoder_driver);
+}
+module_init(maru_dtv_decoder_init);
+module_exit(maru_tuner_decoder_exit);
diff --git a/drivers/maru/tv/maru_sif.c b/drivers/maru/tv/maru_sif.c
new file mode 100644 (file)
index 0000000..5c3e322
--- /dev/null
@@ -0,0 +1,1154 @@
+/*
+ * MARU Virtual SIF Sound Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@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/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+//#include <linux/math64.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <linux/miscdevice.h>
+#include "maru_sif.h"
+
+static int sif_debug = 0;
+module_param(sif_debug, int, 0644);
+MODULE_PARM_DESC(sif_debug, "Turn on/off maru sif debugging (default:off).");
+
+#define print_err(fmt, arg...) \
+       printk(KERN_ERR "marusif: (%s) " fmt, __func__, ##arg)
+
+#define print_warn(fmt, arg...) \
+       printk(KERN_WARNING "marusif: (%s) " fmt, __func__, ##arg)
+
+#define print_info(fmt, arg...) \
+       printk(KERN_INFO "marusif: (%s) " fmt, __func__, ##arg)
+
+#define print_dbg(level, fmt, arg...) \
+       do { \
+               if (sif_debug >= (level)) { \
+                       printk(KERN_INFO "marusif: (%s:%d) " fmt, \
+                                       __func__, __LINE__, ##arg); \
+               } \
+       } while (0)
+
+struct snd_marusif {
+       struct snd_card *card;
+
+       //struct snd_pcm *pcm;
+       //struct snd_pcm_hardware pcm_hw;
+
+       /* sif emulation */
+       SdSif_Settings_t SD_SifSettings;
+       SdSif_SoundSys_k eSoundSys;
+
+       dev_uldSifData_t SifData;
+       Sif_Config_s conf;
+       int bSifStarted;
+       Sif_Standard_e eSifStandard;
+};
+
+/*
+ * data conversion util
+ */
+static int devSif_ConvSifSystemSd2Spi(SdStereoSystem_k eSdSifStd)
+{
+       Sif_System_e eSifSystem = SIF_SYSTEM_MAX;
+
+       switch (eSdSifStd) {
+       case SD_STEREO_SYS_BTSC:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_EIAJ:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_A2_KOREA:
+               eSifSystem = SIF_SYSTEM_M;
+               break;
+
+       case SD_STEREO_SYS_BG_A2:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_BG:
+               eSifSystem = SIF_SYSTEM_BG;
+               break;
+
+       case SD_STEREO_SYS_DK1_A2:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_DK2_A2:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_DK3_A2:
+               /* FALL THROUGH */
+       case SD_STEREO_SYS_DK:
+               eSifSystem = SIF_SYSTEM_DK;
+               break;
+
+       case SD_STEREO_SYS_I:
+               eSifSystem = SIF_SYSTEM_I;
+               break;
+
+       case SD_STEREO_SYS_L:
+               eSifSystem = SIF_SYSTEM_L;
+               break;
+
+       default:
+               eSifSystem = SIF_SYSTEM_MAX;
+               break;
+       }
+
+       return eSifSystem;
+}
+
+/* get mts mode, just simulation to mono */
+static int get_mts_mode(struct snd_marusif *marusif)
+{
+       Sif_MtsMode_e mts_mode;
+
+       switch(marusif->conf.eRegion) {
+       case SIF_REGION_KOREA:
+       case SIF_REGION_USA:
+       case SIF_REGION_JAPAN:
+               //staSif_GetMtsModeForM(pstrtSifHndl);
+               mts_mode = SD_MTS_MONO;
+               break;
+
+       case SIF_REGION_EURO:
+               //staSif_GetMtsModeForPAL(pstrtSifHndl);
+               mts_mode = SD_MTS_MONO;
+               break;
+
+       case SIF_REGION_SEASIA:
+       case SIF_REGION_CHINA:
+               mts_mode = SD_MTS_MONO;
+               break;
+
+       case SIF_REGION_SAMERICA:
+               mts_mode = SD_MTS_MONO;
+               break;
+
+       default :
+               mts_mode = SD_MTS_MONO;
+               break;
+       }
+
+       return mts_mode;
+}
+
+static int devSif_ConvMtsModeSpi2Sd(struct snd_marusif *marusif, Sif_MtsMode_e eSpiMtsMode, SdStereoSystem_k eSdSifStd)
+{
+       SdAnalogMtsMode_k eSdMtsMode = SD_MTS_MAX;
+
+       if (marusif->SifData.SoundSys == SOUND_SYS_TYPE_MAX) {
+               return eSdMtsMode;
+       }
+
+       if (((marusif->SifData.SoundSys == SOUND_SYS_TYPE_3)
+            || (marusif->SifData.SoundSys == SOUND_SYS_TYPE_4)
+            || (marusif->SifData.SoundSys == SOUND_SYS_TYPE_5))
+                       && (devSif_ConvSifSystemSd2Spi(eSdSifStd) == SIF_SYSTEM_M)) {
+               eSdMtsMode= SD_MTS_MONO;
+       }
+
+       switch (eSpiMtsMode) {
+       case SIF_MTS_MONO:
+               eSdMtsMode = SD_MTS_MONO;
+               break;
+
+       case SIF_MTS_STEREO:
+               eSdMtsMode = SD_MTS_STEREO;
+               break;
+
+       case SIF_MTS_DUALMONO:
+               if (SOUND_SYS_TYPE_0 == marusif->SifData.SoundSys) {
+                       eSdMtsMode = SD_MTS_LANGUAGE1;
+               } else if (SOUND_SYS_TYPE_1 == marusif->SifData.SoundSys) {
+                       eSdMtsMode = SD_MTS_SAP;
+               } else {
+                       eSdMtsMode = SD_MTS_DUAL1;
+               }
+               break;
+
+       case SIF_MTS_STEREO_DUALMONO:
+               eSdMtsMode = SD_MTS_STEREO_SAP;
+               break;
+
+       case SIF_MTS_MONO_NICAM:
+               eSdMtsMode = SD_MTS_MONO_NICAM;
+               break;
+
+       default:
+               return SD_MTS_MAX;
+       }
+
+       return eSdMtsMode;
+}
+
+static int devSif_ConvSifStdSd2Spi(SdStereoSystem_k eSdSifStd)
+{
+       Sif_Standard_e eSifStd = SIF_STD_MAX;
+
+       switch (eSdSifStd) {
+       case SD_STEREO_SYS_BTSC:
+               eSifStd = SIF_STD_BTSC;
+               break;
+       case SD_STEREO_SYS_EIAJ:
+               eSifStd = SIF_STD_EIAJ;
+               break;
+       case SD_STEREO_SYS_A2_KOREA:
+               eSifStd = SIF_STD_A2K;
+               break;
+       case SD_STEREO_SYS_BG_A2:
+               eSifStd = SIF_STD_A2_BG;
+               break;
+       case SD_STEREO_SYS_DK1_A2:
+               eSifStd = SIF_STD_A2_DK1;
+               break;
+       case SD_STEREO_SYS_DK2_A2:
+               eSifStd = SIF_STD_A2_DK2;
+               break;
+       case SD_STEREO_SYS_DK3_A2:
+               eSifStd = SIF_STD_A2_DK3;
+               break;
+       case SD_STEREO_SYS_I:
+               eSifStd = SIF_STD_NICAM_I;
+               break;
+       case SD_STEREO_SYS_BG:
+               eSifStd = SIF_STD_NICAM_BG;
+               break;
+       case SD_STEREO_SYS_DK:
+               eSifStd = SIF_STD_NICAM_DK;
+               break;
+       case SD_STEREO_SYS_L:
+               eSifStd = SIF_STD_NICAM_L;
+               break;
+       case SD_STEREO_SYS_UNKNOWN:
+               eSifStd = SIF_STD_ASD;
+               break;
+       default:
+               eSifStd = SIF_STD_MAX;
+               break;
+       }
+
+       return eSifStd;
+}
+
+static int devSif_ConvGetSifRegion(struct snd_marusif *marusif, SdSif_SoundSys_k SdSoundSystem)
+{
+       Sif_Region_e eSifRgn = SIF_REGION_MAX;
+
+       if(SD_SIF_SOUND_SYS_MAX != SdSoundSystem) {
+               switch (SdSoundSystem) {
+               case SD_SIF_SOUND_SYS_0:
+                       eSifRgn = SIF_REGION_KOREA;
+                       break;
+
+               case SD_SIF_SOUND_SYS_1:
+                       eSifRgn = SIF_REGION_USA;
+                       break;
+
+               case SD_SIF_SOUND_SYS_2:
+                       eSifRgn = SIF_REGION_EURO;
+                       break;
+
+               case SD_SIF_SOUND_SYS_3:
+                       /* FALL THROUGH */
+               case SD_SIF_SOUND_SYS_4:
+                       /* FALL THROUGH */
+               case SD_SIF_SOUND_SYS_5:
+                       /* FALL THROUGH */
+                       eSifRgn = SIF_REGION_SEASIA;
+                       break;
+
+               default:
+                       eSifRgn = SIF_REGION_MAX;
+                       break;
+               }
+       }
+
+       return eSifRgn;
+}
+
+static int devSif_ConvSifSystemSpi2Sd(struct snd_marusif *marusif, Sif_System_e eSpiSifSystem)
+{
+       SdStereoSystem_k eSdSoundStd = SD_STEREO_SYS_MAX;
+
+       if (SOUND_SYS_TYPE_MAX == marusif->SifData.SoundSys)
+               return SD_STEREO_SYS_MAX;
+
+       if (SOUND_SYS_TYPE_0 == marusif->SifData.SoundSys)
+               return SD_STEREO_SYS_A2_KOREA;
+
+       if (SOUND_SYS_TYPE_1 == marusif->SifData.SoundSys)
+               return SD_STEREO_SYS_BTSC;
+
+       switch (eSpiSifSystem) {
+       case SIF_SYSTEM_M:
+               if ((SOUND_SYS_TYPE_0 == marusif->SifData.SoundSys)||(SOUND_SYS_TYPE_4 == marusif->SifData.SoundSys)) {
+                       eSdSoundStd = SD_STEREO_SYS_A2_KOREA;
+               } else if (SOUND_SYS_TYPE_1 == marusif->SifData.SoundSys || SOUND_SYS_TYPE_5 == marusif->SifData.SoundSys) {
+                       eSdSoundStd = SD_STEREO_SYS_BTSC;
+               } else {
+                       eSdSoundStd = SD_STEREO_SYS_UNKNOWN;
+               }
+               break;
+
+       case SIF_SYSTEM_BG:
+               eSdSoundStd = SD_STEREO_SYS_BG;
+               break;
+
+       case SIF_SYSTEM_DK:
+               eSdSoundStd = SD_STEREO_SYS_DK;
+               break;
+
+       case SIF_SYSTEM_I:
+               eSdSoundStd = SD_STEREO_SYS_I;
+               break;
+
+       case SIF_SYSTEM_L:
+               eSdSoundStd = SD_STEREO_SYS_L;
+               break;
+
+       default:
+               eSdSoundStd = SD_STEREO_SYS_UNKNOWN;
+               break;
+       }
+
+       return eSdSoundStd;
+}
+
+/*
+ * Maru Amp has no PCM interface
+ */
+
+/*
+ * mixer interface
+ */
+static int marusif_manual_debug_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+       return 0;
+}
+
+static int marusif_manual_debug_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+       return 0;
+}
+
+static int marusif_manual_debug_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+/* "sif mts" */
+static int marusif_mts_status_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[10] = {
+               // the order should match with SdAnalogMtsMode_k
+               "MONO",         //0     < Set Multi Sound Mode to MONO.
+               "MONO_NICAM",   //1     < Set Multi Sound Mode to NICAM MONO.
+               "STEREO",       //2     < Set Multi Sound Mode to STEREO.
+               "SAP",          //3     < Set Multi Sound Mode to SAP.
+               "STEREO_SAP",   //4     < Set Multi Sound Mode to STEREO SAP.
+               "LANGUAGE1",    //5     < Set Multi Sound Mode to LANGUAGE1.
+               "LANGUAGE2",    //6     < Set Multi Sound Mode to LANGUAGE2.
+               "DUAL1",        //7
+               "SD_MTS_DUAL2", //8
+               "NONE",         //9
+       };
+
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 10;
+       if (uinfo->value.enumerated.item >= 9)
+               uinfo->value.enumerated.item = 9;
+
+       strncpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item],strlen(texts[uinfo->value.enumerated.item])+1);
+
+       return 0;
+}
+
+/* sif mts */
+static int marusif_mts_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       marusif->conf.eMtsMode = get_mts_mode(marusif);
+       ucontrol->value.enumerated.item[0] = devSif_ConvMtsModeSpi2Sd(marusif, marusif->conf.eMtsMode, marusif->SD_SifSettings.eStereoSystem);
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_mts_status_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+/* "sif asd" */
+static int marusif_sound_sys_status_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[13] = {
+               // SdStereoSystem_k
+               "SD_STEREO_SYS_BTSC",     //0   < TV-Sys: M/N, Audio Modulation: BTSC-Stereo + SAP
+               "SD_STEREO_SYS_EIAJ",     //1   < TV-Sys: M/N, Audio Modulation: FM-FM (EIAJ)
+               "SD_STEREO_SYS_A2_KOREA", //2   < TV-Sys: M/N, Audio Modulation: FM-Stereo
+               "SD_STEREO_SYS_BG_A2",    //3   < TV-Sys: B/G, Audio Modulation: FM-A2
+               "SD_STEREO_SYS_DK1_A2",   //4   < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K1)
+               "SD_STEREO_SYS_DK2_A2",   //5   < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K2)
+               "SD_STEREO_SYS_DK3_A2",   //6   < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K3)
+               "SD_STEREO_SYS_I",        //7   < TV-Sys: I,   Audio Modulation: FM-Mono/NICAM
+               "SD_STEREO_SYS_BG",       //8   < TV-Sys: B/G, Audio Modulation: FM-Mono/NICAM
+               "SD_STEREO_SYS_DK",       //9   < TV-Sys: D/K, Audio Modulation: FM-Mono/NICAM (D/K)
+               "SD_STEREO_SYS_L",        //10  < TV-Sys: L,   Audio Modulation: AM-Mono/NICAM
+               "SD_STEREO_SYS_UNKNOWN",  //11
+               "SD_STEREO_SYS_MAX",      //12
+       };
+
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 13;
+       if (uinfo->value.enumerated.item >= 12)
+               uinfo->value.enumerated.item = 12;
+
+       strncpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item],strlen(texts[uinfo->value.enumerated.item])+1);
+
+       return 0;
+}
+
+static int marusif_sound_sys_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = devSif_ConvSifSystemSpi2Sd(marusif, marusif->conf.eSystem);
+
+       marusif->SD_SifSettings.eStereoSystem = SD_STEREO_SYS_MAX;
+       marusif->bSifStarted = false;
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_sound_sys_status_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+/* "sif start" */
+static int marusif_start_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+       return 0;
+}
+
+static int marusif_start_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       //struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+
+static int marusif_start_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       if (marusif->bSifStarted == true)
+               return 0;
+
+       marusif->conf.eStandard = marusif->eSifStandard;
+       marusif->conf.eSystem = devSif_ConvSifSystemSd2Spi(marusif->SD_SifSettings.eStereoSystem);
+       marusif->conf.eRegion = devSif_ConvGetSifRegion(marusif, marusif->eSoundSys);
+       marusif->conf.eMtsMode = SIF_MTS_MONO;
+       marusif->conf.eDeviation = (marusif->SD_SifSettings.bHighDeviation) ? SIF_DEV_400: SIF_DEV_200;
+
+       marusif->bSifStarted = true;
+
+       return 0;
+}
+
+/* "sif stop" */
+static int marusif_stop_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+       return 0;
+}
+
+
+/*static int marusif_stop_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+  {
+
+  return 0;
+  }*/
+
+static int marusif_stop_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       marusif->bSifStarted = false;
+
+       return 0;
+}
+
+/* "sif set soundsystem" */
+static int marusif_set_sound_system_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[13] = {
+               // SdStereoSystem_k
+               "SD_STEREO_SYS_BTSC",      //0  < TV-Sys: M/N, Audio Modulation: BTSC-Stereo + SAP
+               "SD_STEREO_SYS_EIAJ",      //1  < TV-Sys: M/N, Audio Modulation: FM-FM (EIAJ)
+               "SD_STEREO_SYS_A2_KOREA",  //2  < TV-Sys: M/N, Audio Modulation: FM-Stereo
+               "SD_STEREO_SYS_BG_A2",     //3  < TV-Sys: B/G, Audio Modulation: FM-A2
+               "SD_STEREO_SYS_DK1_A2",    //4  < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K1)
+               "SD_STEREO_SYS_DK2_A2",    //5  < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K2)
+               "SD_STEREO_SYS_DK3_A2",    //6  < TV-Sys: D/K, Audio Modulation: FM-A2 (D/K3)
+               "SD_STEREO_SYS_I",         //7  < TV-Sys: I,   Audio Modulation: FM-Mono/NICAM
+               "SD_STEREO_SYS_BG",        //8  < TV-Sys: B/G, Audio Modulation: FM-Mono/NICAM
+               "SD_STEREO_SYS_DK",        //9  < TV-Sys: D/K, Audio Modulation: FM-Mono/NICAM (D/K)
+               "SD_STEREO_SYS_L",         //10 < TV-Sys: L,   Audio Modulation: AM-Mono/NICAM
+               "SD_STEREO_SYS_UNKNOWN",   //11
+               "SD_STEREO_SYS_MAX",       //12
+       };
+
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 13;
+       if (uinfo->value.enumerated.item >= 12)
+               uinfo->value.enumerated.item = 12;
+
+       strncpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item],strlen(texts[uinfo->value.enumerated.item])+1);
+
+       return 0;
+}
+
+static int marusif_set_sound_system_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = marusif->SD_SifSettings.eStereoSystem;
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_set_sound_system_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       marusif->conf.eStandard = devSif_ConvSifStdSd2Spi((SdStereoSystem_k)ucontrol->value.integer.value[0]);
+
+       if (SIF_STD_ASD == marusif->conf.eStandard) {
+               marusif->conf.eMtsMode = SIF_MTS_MONO;
+               marusif->conf.eDeviation = (marusif->SD_SifSettings.bHighDeviation) ? SIF_DEV_400: SIF_DEV_200;
+               marusif->eSifStandard = marusif->conf.eStandard;
+       }
+
+       if (SIF_STD_MAX != marusif->conf.eStandard)
+               marusif->eSifStandard = marusif->conf.eStandard;
+
+       marusif->SD_SifSettings.eStereoSystem = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+/* "sif soundsys" */
+static int marusif_soundsys_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[16] = {
+               "SD_SIF_SOUND_SYS_0",   ///<M (mono system), A2(stereo system) : KOR
+               "SD_SIF_SOUND_SYS_1",   ///<M (mono system), BTSC(stereo system) : USA, BRA, TAIWAN
+               "SD_SIF_SOUND_SYS_2",   ///<BG,DK,I,L (mono system), A2_BG,A2_DK1,A2_DK3,NICAM_BG,NICAM_I,NICAM_DK,NICAM_L(stereo system)  : PANEURO, PANNORDIG
+               "SD_SIF_SOUND_SYS_3",   ///<BG,DK,I,M (mono system), A2_BG,NICAM_BG,NICAM_I,NICAM_DK(stereo system) : S.PACIFIC, ARB, S.ASIA, AsiaATV, C.ASIA
+               "SD_SIF_SOUND_SYS_4",   ///<DK (mono system), A2_DK2(stereo system)  : CHINA
+               "SD_SIF_SOUND_SYS_5",   ///<I (mono system), NICAM_I(stereo system)  : HKG
+               "SD_SIF_SOUND_SYS_6",   ///< reserved
+               "SD_SIF_SOUND_SYS_7",   ///< reserved
+               "SD_SIF_SOUND_SYS_8",   ///< reserved
+               "SD_SIF_SOUND_SYS_9",   ///< reserved
+               "SD_SIF_SOUND_SYS_10",  ///< reserved
+               "SD_SIF_SOUND_SYS_11",  ///< reserved
+               "SD_SIF_SOUND_SYS_12",  ///< reserved
+               "SD_SIF_SOUND_SYS_13",  ///< reserved
+               "SD_SIF_SOUND_SYS_14",  ///< reserved
+               "SD_SIF_SOUND_SYS_NONE"
+       };
+
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 16;
+       if (uinfo->value.enumerated.item >= 15)
+               uinfo->value.enumerated.item = 15;
+
+       strncpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item],strlen(texts[uinfo->value.enumerated.item])+1);
+
+       return 0;
+}
+
+static int marusif_soundsys_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = marusif->eSoundSys;
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_soundsys_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       if (ucontrol->value.integer.value[0] >= SOUND_SYS_TYPE_MAX) {
+               print_err("out of range.\n");
+               return -1;
+       }
+
+       marusif->eSoundSys = (Sif_SoundSys_e)ucontrol->value.integer.value[0];
+       marusif->SifData.SoundSys = marusif->eSoundSys;
+
+       return 0;
+}
+
+/* "sif mts out mode" */
+static int marusif_mts_out_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[10] = {
+               // the order should match with SdAnalogMtsMode_k
+               "MONO",          ///< Set Multi Sound Mode to MONO.
+               "MONO_NICAM",    ///< Set Multi Sound Mode to NICAM MONO.
+               "STEREO",        ///< Set Multi Sound Mode to STEREO.
+               "SAP",           ///< Set Multi Sound Mode to SAP.
+               "STEREO_SAP",    ///< Set Multi Sound Mode to STEREO SAP.
+               "LANGUAGE1",     ///< Set Multi Sound Mode to LANGUAGE1.
+               "LANGUAGE2",     ///< Set Multi Sound Mode to LANGUAGE2.
+               "DUAL1",
+               "SD_MTS_DUAL2",
+               "NONE"
+       };
+
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 10;
+       if (uinfo->value.enumerated.item >= 9)
+               uinfo->value.enumerated.item = 9;
+
+       strncpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item],strlen(texts[uinfo->value.enumerated.item])+1);
+
+       return 0;
+}
+
+static int marusif_mts_out_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = marusif->SD_SifSettings.eOutputMts;
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_mts_out_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       if (ucontrol->value.integer.value[0] >= SD_MTS_MAX) {
+               print_err("out of range.\n");
+               return -1;
+       }
+
+       marusif->SD_SifSettings.eOutputMts = ucontrol->value.enumerated.item[0];
+
+       return 0;
+}
+
+/* "sif carrier mute" */
+static int marusif_carrier_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+static int marusif_carrier_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = marusif->SD_SifSettings.bCarrierMute;
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+static int marusif_carrier_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       if (ucontrol->value.integer.value[0] >= SIF_EN_MAX) {
+               print_err("out of range.\n");
+               return -1;
+       }
+
+       marusif->SD_SifSettings.bCarrierMute = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+/* "sif high deviation" */
+static int marusif_high_deviation_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+static int marusif_high_deviation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = marusif->SD_SifSettings.bHighDeviation;
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       return 0;
+}
+
+static int marusif_high_deviation_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+
+       if (ucontrol->value.integer.value[0] >= SIF_DEV_MAX) {
+               print_err("out of range.\n");
+               return -1;
+       }
+
+       marusif->SD_SifSettings.bHighDeviation = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+/* "sif pilot highlow" */
+static int marusif_pilot_highlow_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+       print_dbg(1, "process:%s(%d)\n", current->comm, current->pid);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xFFFF;
+
+       return 0;
+}
+
+static int marusif_pilot_highlow_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = marusif->SD_SifSettings.pilotVal_Low;
+       ucontrol->value.integer.value[1] = marusif->SD_SifSettings.pilotVal_Hi;
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       return 0;
+}
+
+static int marusif_pilot_highlow_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_marusif *marusif = snd_kcontrol_chip(kcontrol);
+
+       print_dbg(1, "process:%s(%d) val=%ld\n",
+                       current->comm, current->pid, ucontrol->value.integer.value[0]);
+       marusif->SD_SifSettings.pilotVal_Low = ucontrol->value.integer.value[0];
+       marusif->SD_SifSettings.pilotVal_Hi= ucontrol->value.integer.value[1];
+
+       return 0;
+}
+
+static struct snd_kcontrol_new marusif_init_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif manual debug",
+               .info = marusif_manual_debug_info,
+               .get = marusif_manual_debug_get,
+               .put = marusif_manual_debug_put,
+               //.private_value = (unsigned long)marusif_mc_priv
+       },
+};
+
+static struct snd_kcontrol_new marusif_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif mts",
+               .info = marusif_mts_status_info,
+               .get = marusif_mts_status_get,
+               .put = marusif_mts_status_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif asd",
+               .info = marusif_sound_sys_status_info,
+               .get = marusif_sound_sys_status_get,
+               .put = marusif_sound_sys_status_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif start",
+               .info = marusif_start_info,
+               .get = marusif_start_get,
+               .put = marusif_start_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif stop",
+               .info = marusif_stop_info,
+               .get = marusif_stop_put, //XXX : is this intended???
+               .put = marusif_stop_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif set soundsystem",
+               .info = marusif_set_sound_system_info,
+               .get = marusif_set_sound_system_get,
+               .put = marusif_set_sound_system_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif soundsys",
+               .info = marusif_soundsys_info,
+               .get = marusif_soundsys_get,
+               .put = marusif_soundsys_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif mts out mode",
+               .info = marusif_mts_out_mode_info,
+               .get = marusif_mts_out_mode_get,
+               .put = marusif_mts_out_mode_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif carrier mute",
+               .info = marusif_carrier_mute_info,
+               .get = marusif_carrier_mute_get,
+               .put = marusif_carrier_mute_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif high deviation",
+               .info = marusif_high_deviation_info,
+               .get = marusif_high_deviation_get,
+               .put = marusif_high_deviation_put,
+       },
+
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "sif pilot highlow",
+               .info = marusif_pilot_highlow_info,
+               .get = marusif_pilot_highlow_get,
+               .put = marusif_pilot_highlow_put,
+       },
+};
+
+static int marusif_new_mixer(struct snd_marusif *marusif)
+{
+       struct snd_card *card = marusif->card;
+       unsigned int idx;
+       int err;
+
+       strcpy(card->mixername, "Marusif Mixer");
+
+       for (idx = 0; idx < ARRAY_SIZE(marusif_init_controls); idx++) {
+               err = snd_ctl_add(card, snd_ctl_new1(&marusif_init_controls[idx], marusif));
+               if (err < 0) {
+                       print_err("failed to add marusif_init_controls, idx=%d, err=%d\n", idx, err);
+                       return err;
+               }
+       }
+
+       for (idx = 0; idx < ARRAY_SIZE(marusif_controls); idx++) {
+               err = snd_ctl_add(card, snd_ctl_new1(&marusif_controls[idx], marusif));
+               if (err < 0) {
+                       print_err("failed to add marusif_controls, idx=%d, err=%d\n", idx, err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+#if 0 /* TODO */
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+/*
+ * proc interface
+ */
+static void print_formats(struct snd_marusif *marusif,
+               struct snd_info_buffer *buffer)
+{
+       int i;
+
+       for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+               if (marusif->pcm_hw.formats & (1ULL << i))
+                       snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
+       }
+}
+
+static void print_rates(struct snd_marusif *marusif,
+               struct snd_info_buffer *buffer)
+{
+       static int rates[] = {
+               5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+               64000, 88200, 96000, 176400, 192000,
+       };
+       int i;
+
+       if (marusif->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
+               snd_iprintf(buffer, " continuous");
+       if (marusif->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
+               snd_iprintf(buffer, " knot");
+       for (i = 0; i < ARRAY_SIZE(rates); i++)
+               if (marusif->pcm_hw.rates & (1 << i))
+                       snd_iprintf(buffer, " %d", rates[i]);
+}
+
+#define get_marusif_int_ptr(marusif, ofs) \
+       (unsigned int *)((char *)&((marusif)->pcm_hw) + (ofs))
+#define get_marusif_ll_ptr(marusif, ofs) \
+       (unsigned long long *)((char *)&((marusif)->pcm_hw) + (ofs))
+
+struct marusif_hw_field {
+       const char *name;
+       const char *format;
+       unsigned int offset;
+       unsigned int size;
+};
+#define FIELD_ENTRY(item, fmt) {                  \
+       .name = #item,                             \
+       .format = fmt,                             \
+       .offset = offsetof(struct snd_pcm_hardware, item), \
+       .size = sizeof(marusif_pcm_hardware.item) }
+
+static struct marusif_hw_field fields[] = {
+       FIELD_ENTRY(formats, "%#llx"),
+       FIELD_ENTRY(rates, "%#x"),
+       FIELD_ENTRY(rate_min, "%d"),
+       FIELD_ENTRY(rate_max, "%d"),
+       FIELD_ENTRY(channels_min, "%d"),
+       FIELD_ENTRY(channels_max, "%d"),
+       FIELD_ENTRY(buffer_bytes_max, "%ld"),
+       FIELD_ENTRY(period_bytes_min, "%ld"),
+       FIELD_ENTRY(period_bytes_max, "%ld"),
+       FIELD_ENTRY(periods_min, "%d"),
+       FIELD_ENTRY(periods_max, "%d"),
+};
+
+static void marusif_proc_read(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct snd_marusif *marusif = entry->private_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fields); i++) {
+               snd_iprintf(buffer, "%s ", fields[i].name);
+               if (fields[i].size == sizeof(int))
+                       snd_iprintf(buffer, fields[i].format,
+                                       *get_marusif_int_ptr(marusif, fields[i].offset));
+               else
+                       snd_iprintf(buffer, fields[i].format,
+                                       *get_marusif_ll_ptr(marusif, fields[i].offset));
+               if (!strcmp(fields[i].name, "formats"))
+                       print_formats(marusif, buffer);
+               else if (!strcmp(fields[i].name, "rates"))
+                       print_rates(marusif, buffer);
+               snd_iprintf(buffer, "\n");
+       }
+}
+
+static void marusif_proc_write(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct snd_marusif *marusif = entry->private_data;
+       char line[64];
+
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               char item[20];
+               const char *ptr;
+               unsigned long long val;
+               int i;
+
+               ptr = snd_info_get_str(item, line, sizeof(item));
+               for (i = 0; i < ARRAY_SIZE(fields); i++) {
+                       if (!strcmp(item, fields[i].name))
+                               break;
+               }
+               if (i >= ARRAY_SIZE(fields))
+                       continue;
+               snd_info_get_str(item, ptr, sizeof(item));
+               if (strict_strtoull(item, 0, &val))
+                       continue;
+               if (fields[i].size == sizeof(int))
+                       *get_marusif_int_ptr(marusif, fields[i].offset) = val;
+               else
+                       *get_marusif_ll_ptr(marusif, fields[i].offset) = val;
+       }
+}
+
+static void marusif_proc_init(struct snd_marusif *chip)
+{
+       struct snd_info_entry *entry;
+
+       if (!snd_card_proc_new(chip->card, "marusif_pcm", &entry)) {
+               snd_info_set_text_ops(entry, chip, marusif_proc_read);
+               entry->c.text.write = marusif_proc_write;
+               entry->mode |= S_IWUSR;
+               entry->private_data = chip;
+       }
+}
+#else
+#define marusif_proc_init(x)
+#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+#endif
+
+static struct miscdevice snd_marusif_dev = {
+       MISC_DYNAMIC_MINOR,
+       "dtv_sif",
+};
+
+static int __init marusif_init(void)  //struct device *dev
+{
+       struct snd_card *card;
+       struct snd_marusif *marusif;
+       //int dev;
+       int err;
+
+       err = misc_register(&snd_marusif_dev);
+       if (err < 0) {
+               print_err("can't misc_register on minor=%d\n",
+                               MISC_DYNAMIC_MINOR);
+               return -1;
+       }
+       //dev = snd_marusif_dev.this_device->id;
+
+       /* card0 : reserved by AC97 */
+       err = snd_card_new(snd_marusif_dev.this_device, 2, NULL, THIS_MODULE,
+                       sizeof(struct snd_marusif), &card);
+       if (err < 0)
+               return err;
+
+       marusif = card->private_data;
+       marusif->card = card;
+
+       err = marusif_new_mixer(marusif);
+       if (err < 0)
+               goto error;
+
+       strcpy(card->driver, "marusif");
+       strcpy(card->shortname, "marusif");
+       snprintf(card->longname, sizeof(card->longname), "Maru Virtual Sif");
+
+       //marusif_proc_init(marusif);
+
+//     snd_card_set_dev(card, snd_marusif_dev.this_device);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       print_info("initialized\n");
+
+       return err;
+
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void __exit marusif_exit(void)
+{
+       //snd_card_free(platform_get_drvdata(devptr));
+       misc_deregister(&snd_marusif_dev);
+}
+
+module_init(marusif_init)
+module_exit(marusif_exit)
diff --git a/drivers/maru/tv/maru_sif.h b/drivers/maru/tv/maru_sif.h
new file mode 100644 (file)
index 0000000..d7adb72
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * MARU Virtual SIF Sound Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@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
+ *
+ */
+
+
+/*
+ * porting structure
+ */
+typedef enum
+{
+       SIF_DISABLE,
+       SIF_ENABLE,
+
+       SIF_EN_MAX
+} Sif_Enable_e;
+
+typedef enum
+{
+       SIF_DEV_100,
+       SIF_DEV_200,
+       SIF_DEV_400,
+
+       SIF_DEV_MAX
+} Sif_Deviation_e;
+
+typedef enum {
+       SOUND_SYS_TYPE_0,   ///<M (mono system), A2(stereo system) : KOR
+       SOUND_SYS_TYPE_1,   ///<M (mono system), BTSC(stereo system) : USA, BRA, TAIWAN
+       SOUND_SYS_TYPE_2,   ///<BG,DK,I,L (mono system), A2_BG,A2_DK1,A2_DK3,NICAM_BG,NICAM_I,NICAM_DK,NICAM_L(stereo system)  : PANEURO, PANNORDIG
+       SOUND_SYS_TYPE_3,   ///<BG,DK,I,M (mono system), A2_BG,NICAM_BG,NICAM_I,NICAM_DK(stereo system) : S.PACIFIC, ARB, S.ASIA, AsiaATV, C.ASIA
+       SOUND_SYS_TYPE_4,   ///<DK,M (mono system), A2_DK2(stereo system)  : CHINA
+       SOUND_SYS_TYPE_5,   ///<I,M (mono system), NICAM_I(stereo system)  : HKG
+       SOUND_SYS_TYPE_6,   ///< reserved
+       SOUND_SYS_TYPE_7,  ///< reserved
+       SOUND_SYS_TYPE_8,  ///< reserved
+       SOUND_SYS_TYPE_9,  ///< reserved
+       SOUND_SYS_TYPE_10,  ///< reserved
+       SOUND_SYS_TYPE_11,  ///< reserved
+       SOUND_SYS_TYPE_12,  ///< reserved
+       SOUND_SYS_TYPE_13,  ///< reserved
+       SOUND_SYS_TYPE_14,  ///< reserved
+       SOUND_SYS_TYPE_MAX
+} Sif_SoundSys_e;
+
+typedef struct _dev_uldSifData
+{
+#if 0 //not emulated
+       Sif_DefaultParam_s      sif_Mono;
+       Sif_DefaultParam_s      sif_Stereo;
+       Sif_DefaultParam_s      sif_Dual2;
+       Sif_Thlds_s             sif_thld;
+       Sif_FactoryThlds_s      sif_usrthld;
+       Sif_Scaler_s            sif_Scaler;
+       Sif_Enable_e            CarrierMuteEn;
+       Sif_Deviation_e         HiDevOnOff;
+       int                     firstChatteringNum;
+       int                     secondChatteringNum;
+       Sif_Enable_e            CIBparity;
+       UInt32                  cutoffgain_ntsc;
+       UInt32                  cutoffgain_pal;
+#endif
+       Sif_SoundSys_e          SoundSys;
+} dev_uldSifData_t;
+
+typedef enum
+{
+       SIF_STD_BTSC,
+       SIF_STD_A2K,
+       SIF_STD_EIAJ,
+       SIF_STD_NICAM_I,
+       SIF_STD_NICAM_DK,
+       SIF_STD_NICAM_BG,
+       SIF_STD_NICAM_L,
+       SIF_STD_A2_DK1,
+       SIF_STD_A2_DK2,
+       SIF_STD_A2_DK3,
+       SIF_STD_A2_BG,
+       SIF_STD_I_MONO,
+       SIF_STD_L_MONO,
+       SIF_STD_ASD,
+       SIF_STD_PALN,
+
+       SIF_STD_MAX
+} Sif_Standard_e;
+
+typedef enum
+{
+       SIF_MTS_MONO=0,
+       SIF_MTS_STEREO=1,
+       SIF_MTS_FOREIGN=2,
+       SIF_MTS_STEREO_DUALMONO=3,
+       SIF_MTS_MONO_NICAM=4,
+       SIF_MTS_DUALMONO=6,
+       // in BTSC, stereo and dualmono can be detected at the same time.
+
+       SIF_MTS_MAX=8
+} Sif_MtsMode_e;
+
+typedef enum {
+       SIF_SYSTEM_M,
+       SIF_SYSTEM_BG,
+       SIF_SYSTEM_DK,
+       SIF_SYSTEM_I,
+       SIF_SYSTEM_L,
+       SIF_SYSTEM_AUTO,
+       SIF_SYSTEM_UNKNOWN,
+
+       SIF_SYSTEM_MAX
+} Sif_System_e;
+
+typedef enum {
+       SIF_REGION_KOREA,
+       SIF_REGION_USA,
+       SIF_REGION_EURO,
+       SIF_REGION_SEASIA,
+       SIF_REGION_SAMERICA,
+       SIF_REGION_JAPAN,
+       SIF_REGION_CHINA,
+
+       SIF_REGION_MAX
+} Sif_Region_e;
+
+typedef enum
+{
+       SIF_ALONE,
+       SIF_AND_IF,
+       SIF_PATH_MAX
+} Sif_Path_e;
+
+typedef enum {
+       SD_STEREO_SYS_BTSC,     ///< TV-Sys: M/N, Audio Modulation: BTSC-Stereo + SAP
+       SD_STEREO_SYS_EIAJ,     ///< TV-Sys: M/N, Audio Modulation: FM-FM (EIAJ)
+       SD_STEREO_SYS_A2_KOREA, ///< TV-Sys: M/N, Audio Modulation: FM-Stereo
+       SD_STEREO_SYS_BG_A2,    ///< TV-Sys: B/G, Audio Modulation: FM-A2
+       SD_STEREO_SYS_DK1_A2,   ///< TV-Sys: D/K, Audio Modulation: FM-A2 (D/K1)
+       SD_STEREO_SYS_DK2_A2,   ///< TV-Sys: D/K, Audio Modulation: FM-A2 (D/K2)
+       SD_STEREO_SYS_DK3_A2,   ///< TV-Sys: D/K, Audio Modulation: FM-A2 (D/K3)
+       SD_STEREO_SYS_I,        ///< TV-Sys: I,   Audio Modulation: FM-Mono/NICAM
+       SD_STEREO_SYS_BG,       ///< TV-Sys: B/G, Audio Modulation: FM-Mono/NICAM
+       SD_STEREO_SYS_DK,       ///< TV-Sys: D/K, Audio Modulation: FM-Mono/NICAM (D/K)
+       SD_STEREO_SYS_L,        ///< TV-Sys: L,   Audio Modulation: AM-Mono/NICAM
+       SD_STEREO_SYS_UNKNOWN,
+       SD_STEREO_SYS_MAX
+} SdStereoSystem_k;
+
+typedef enum
+{
+       SD_SIF_SOUND_SYS_0,   ///<M (mono system), A2(stereo system) : KOR
+       SD_SIF_SOUND_SYS_1,   ///<M (mono system), BTSC(stereo system) : USA, BRA, TAIWAN
+       SD_SIF_SOUND_SYS_2,   ///<BG,DK,I,L (mono system), A2_BG,A2_DK1,A2_DK3,NICAM_BG,NICAM_I,NICAM_DK,NICAM_L(stereo system)  : PANEURO, PANNORDIG
+       SD_SIF_SOUND_SYS_3,   ///<BG,DK,I,M (mono system), A2_BG,NICAM_BG,NICAM_I,NICAM_DK(stereo system) : S.PACIFIC, ARB, S.ASIA, AsiaATV, C.ASIA
+       SD_SIF_SOUND_SYS_4,   ///<DK (mono system), A2_DK2(stereo system)  : CHINA
+       SD_SIF_SOUND_SYS_5,   ///<I (mono system), NICAM_I(stereo system)  : HKG
+       SD_SIF_SOUND_SYS_6,   ///< reserved
+       SD_SIF_SOUND_SYS_7,   ///< reserved
+       SD_SIF_SOUND_SYS_8,   ///< reserved
+       SD_SIF_SOUND_SYS_9,   ///< reserved
+       SD_SIF_SOUND_SYS_10,  ///< reserved
+       SD_SIF_SOUND_SYS_11,  ///< reserved
+       SD_SIF_SOUND_SYS_12,  ///< reserved
+       SD_SIF_SOUND_SYS_13,  ///< reserved
+       SD_SIF_SOUND_SYS_14,  ///< reserved
+       SD_SIF_SOUND_SYS_MAX
+} SdSif_SoundSys_k;
+
+typedef enum {
+       SD_MTS_MONO,       ///< Set Multi Sound Mode to MONO.
+       SD_MTS_MONO_NICAM, ///< Set Multi Sound Mode to NICAM MONO.
+       SD_MTS_STEREO,     ///< Set Multi Sound Mode to STEREO.
+       SD_MTS_SAP,        ///< Set Multi Sound Mode to SAP.
+       SD_MTS_STEREO_SAP, ///< Set Multi Sound Mode to STEREO SAP.
+       SD_MTS_LANGUAGE1,  ///< Set Multi Sound Mode to LANGUAGE1.
+       SD_MTS_LANGUAGE2,  ///< Set Multi Sound Mode to LANGUAGE2.
+       SD_MTS_DUAL1,
+       SD_MTS_DUAL2,
+       SD_MTS_MAX
+} SdAnalogMtsMode_k;
+
+typedef struct {
+       SdStereoSystem_k  eStereoSystem;  ///< eOutputAnalogMts should be set after setting of eSoundSystem
+       SdAnalogMtsMode_k eOutputMts;     ///< depends on input MTS mode except Mono input
+       bool              bCarrierMute;   ///< Carrier_Mute On/Off
+       uint32_t          carrierFreq;    ///< Carrier_Mute Freq.
+       bool              bHighDeviation; ///< Hi-Dev On/Off
+       uint32_t          pilotVal_Hi;    ///< pilot high value
+       uint32_t          pilotVal_Low;   ///< pilot low value
+} SdSif_Settings_t;
+
+typedef struct {
+       Sif_Standard_e  eStandard;
+       Sif_Deviation_e eDeviation;
+       Sif_MtsMode_e   eMtsMode;
+       Sif_System_e    eSystem;
+       Sif_Region_e    eRegion;
+       Sif_Path_e      ePath;
+} Sif_Config_s;
diff --git a/drivers/maru/tv/maru_tuner.c b/drivers/maru/tv/maru_tuner.c
new file mode 100644 (file)
index 0000000..f35d857
--- /dev/null
@@ -0,0 +1,1274 @@
+/*
+ * MARU Virtual Tuner Driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@samsung.com>
+ * Ningjia Fan <ningjia.fan@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/init.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-ioctl.h>
+
+#include "maru_tuner.h"
+#include "maru_atv.h"
+
+#define MARUTUNER_DEBUG_LEVEL  0
+
+static int tuner_debug = 0;
+static struct marutuner_dev* main_tuner = NULL;
+module_param(tuner_debug, int, 0644);
+MODULE_PARM_DESC(tuner_debug, "Turn on/off maru tuner debugging (default:off).");
+
+#define print_err(fmt, arg...) \
+       printk(KERN_ERR "marutuner: (%s) " fmt, __func__, ##arg)
+
+#define print_warn(fmt, arg...) \
+       printk(KERN_WARNING "marutuner: (%s) " fmt, __func__, ##arg)
+
+#define print_info(fmt, arg...) \
+       printk(KERN_INFO "marutuner: (%s) " fmt, __func__, ##arg)
+
+#define print_dbg(level, fmt, arg...) \
+       do { \
+               if (tuner_debug >= (level)) { \
+                       printk(KERN_INFO "marutuner: (%s:%d) " fmt, \
+                                       __func__, __LINE__, ##arg); \
+               } \
+       } while (0)
+
+//#define TIME_STAMP_ENABLE
+
+#ifdef TIME_STAMP_ENABLE
+#include <linux/time.h>
+static struct timeval tuned_old_t;
+static struct timeval feed_old_t;
+#endif
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct dvb_adapter *marutuner_adapter = NULL;
+
+struct dvb_adapter *marutuner_get_adapter(void)
+{
+       if (!marutuner_adapter)
+               return NULL;
+
+       return marutuner_adapter;
+}
+EXPORT_SYMBOL(marutuner_get_adapter);
+
+/*****************************************************************************
+ * frontend
+ *****************************************************************************/
+/*
+ * atv tuner
+ */
+#define MARUTUNER_PTC3_FREQ 63000000
+
+static struct v4l2_input tuner_inputs[] = {
+       { 0, "TUNER 1", V4L2_INPUT_TYPE_TUNER,  0, 0, V4L2_STD_NTSC, 0, V4L2_IN_CAP_STD }
+};
+
+static int marutuner_atvtuner_open(struct file *file)
+{
+       int ret = 0;
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+
+       print_dbg(1, "\n");
+
+       file->private_data = marutuner;
+
+       if (fe->ops.init != NULL) {
+               ret = fe->ops.init(fe);
+       } else {
+               print_err("dvb_frontend->ops.init is NULL\n");
+               ret = -1;
+               goto out;
+       }
+out:
+       return ret;
+}
+
+static int marutuner_atvtuner_release(struct file *file)
+{
+       /* nothing to do */
+       print_dbg(1, "\n");
+       return 0;
+}
+
+/* ioctls */
+static int maruatv_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+       int ret = 0;
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       u16 strength;
+
+       print_dbg(1, "\n");
+
+       if (t->index != 0)
+               return -EINVAL;
+
+       strcpy(t->name, "Television");
+
+       t->type = V4L2_TUNER_ANALOG_TV;
+       t->capability = V4L2_TUNER_CAP_NORM;
+       t->rangelow = fe->ops.info.frequency_min / 100 / 625;
+       t->rangehigh = fe->ops.info.frequency_max / 100 / 625;
+
+       /* signal strength */
+       if (fe->ops.tuner_ops.get_rf_strength != NULL) {
+               ret = fe->ops.tuner_ops.get_rf_strength(fe, &(strength));
+               if (ret != 0) {
+                       print_err("get_rf_strength failed %d\n", ret);
+                       t->signal = 0xffff;
+               }
+               t->signal = strength;
+       } else {
+               t->signal = 0xffff;
+       }
+
+       /* afc */
+#if 0 // get_afc function isn't supported on linux-3.4
+       if (fe->ops.tuner_ops.get_afc != NULL) {
+               ret = fe->ops.tuner_ops.get_afc(fe, &(t->afc));
+               if (ret != 0) {
+                       print_err("get_afc failed %d\n", ret);
+               }
+       } else {
+               t->afc = 0; /* unsupport */
+       }
+#else
+       t->afc = 0; /* unsupport */
+#endif
+
+       return 0;
+}
+
+static int maruatv_vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t)
+{
+       if (t->index != 0)
+               return -EINVAL;
+
+       print_dbg(1, "\n");
+
+       return 0;
+}
+
+static int maruatv_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f)
+{
+       int ret = 0;
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+
+       print_dbg(1, "\n");
+
+       if (fe->ops.tuner_ops.get_frequency != NULL) {
+               ret = fe->ops.tuner_ops.get_frequency(fe, &f->frequency);
+       } else {
+               f->frequency = state->current_frequency;
+               print_dbg(1, "freq = %d\n", f->frequency);
+       }
+
+       return 0;
+}
+
+static int maruatv_vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f)
+{
+       int ret = 0;
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+
+       print_dbg(1, "\n");
+
+       if (fe->ops.tuner_ops.set_frequency != NULL) {
+               ret = fe->ops.tuner_ops.set_frequency(fe, f->frequency);
+       } else {
+               writel(f->frequency, marutuner->io_mem + MARUTUNER_ATV_SET_TUNE);
+               state->current_frequency = f->frequency;
+               print_dbg(1, "freq = %d\n", state->current_frequency);
+       }
+
+       return ret;
+}
+
+static int maruatv_vidioc_s_std(struct file *file, void *priv, v4l2_std_id std)
+{
+       print_dbg(1, "\n");
+
+       if (((std) & V4L2_STD_NTSC) == 0)
+               return -EINVAL;
+       else
+               return 0;
+}
+
+static int maruatv_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+       print_dbg(1, "\n");
+
+       *std = V4L2_STD_NTSC;
+
+       return 0;
+}
+
+static int maruatv_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+       print_dbg(1, "\n");
+
+       strlcpy(cap->driver, "marutuner_atv", sizeof(cap->driver));
+       cap->capabilities = V4L2_CAP_TUNER;
+
+       return 0;
+}
+
+static int _maruatv_vidioc_enum_input(struct marutuner_dev* marutuner)
+{
+       u32 st = readl(marutuner->io_mem + MARUTUNER_ATV_GET_STATUS);
+       switch (st & 0xFF) {
+       case MARUTUNER_FE_HAS_ONLY_PARAM:
+               print_dbg(2, "ATV locked\n");
+               return 1;
+       case MARUTUNER_FE_TUNE_FAILED:
+               print_dbg(1, "ATV tune failed, no entry\n");
+               break;
+       case MARUTUNER_FE_HAS_TS:
+               print_dbg(1, "ATV tune failed, dtv channel\n");
+               break;
+       default:
+               print_err("unknown status\n");
+               break;
+       }
+       return 0;
+}
+
+int maruatv_get_status(void)
+{
+       return _maruatv_vidioc_enum_input(main_tuner);
+}
+
+static int maruatv_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input)
+{
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       u32 st = 0;
+       int ret = 0;
+
+       print_dbg(1, "\n");
+
+       if (input->index != 0) {        /* only for one tuner */
+               return -EINVAL;
+       }
+
+       memcpy(input, &tuner_inputs[input->index], sizeof(struct v4l2_input));
+
+       /* check status */
+       if (fe->ops.tuner_ops.get_status != NULL) {
+               ret = fe->ops.tuner_ops.get_status(fe, &st);
+               if (ret == 0) {
+                       if (st & TUNER_STATUS_LOCKED) {
+                               input->status &= ~V4L2_IN_ST_NO_SIGNAL;
+                               //dprintk("%s, Get lock\n", __FUNCTION__);
+                       } else {
+                               input->status |= V4L2_IN_ST_NO_SIGNAL;
+                               //dprintk("%s, No lock\n", __FUNCTION__);
+                       }
+               } else {
+                       print_dbg(1, "get_status failed %d\n", ret);
+               }
+       } else {
+               input->status |= V4L2_IN_ST_NO_SIGNAL;
+               ret = _maruatv_vidioc_enum_input(marutuner);
+               if (ret)
+                       input->status &= ~V4L2_IN_ST_NO_SIGNAL;
+               print_dbg(1, "atv read frontend status, input->status(%d)\n", input->status);
+       }
+
+       return ret;
+}
+
+#if 0 //V4L2_CID_TUNER_LOCK_TIME non-public linux
+static int atv_tuner_get_ctrl(struct dvb_frontend *fe, struct v4l2_ext_control *ctrl)
+{
+       int ret = 0;
+       switch (ctrl->id) {
+       case V4L2_CID_TUNER_LOCK_TIME: /* lock time */
+               ctrl->value = 500; /* ms */
+               ret = 0;
+               break;
+       case V4L2_CID_TUNER_G_FINE_TUNE:
+               /* afd */
+               if (fe->ops.tuner_ops.get_afd != NULL) {
+                       ret = fe->ops.tuner_ops.get_afd(fe, &(ctrl->value));
+                       if (ret != 0) {
+                               printk("get_afd failed %d\n", ret);
+                       }
+               } else {
+                       ret = -EINVAL;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+#endif
+
+
+
+#if 0 // V4L2_CID_TUNER_S_FINE_TUNE non-public linux
+static int atv_tuner_set_ctrl(struct dvb_frontend *fe, struct v4l2_ext_control *ctrl)
+{
+       int ret = 0;
+       switch (ctrl->id) {
+       case V4L2_CID_TUNER_S_FINE_TUNE:
+               /* manual fine tune */
+               if (fe->ops.tuner_ops.set_mft != NULL) {
+                       ret = fe->ops.tuner_ops.set_mft(fe, ctrl->value);
+                       if (ret != 0) {
+                               printk("set_mft failed %d\n", ret);
+                       }
+               } else {
+                       ret = -EINVAL;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+#endif
+
+static int maruatv_vidioc_g_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls)
+{
+#if 0 // V4L2_CTRL_CLASS_TUNER non-public linux
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       int i, err = 0;
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_TUNER) {
+
+               for (i = 0; i < ctrls->count; i++) {
+                       struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+                       err = atv_tuner_get_ctrl(fe, ctrl);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
+       }
+
+       return -EINVAL;
+#endif
+       return 0;
+}
+
+static int maruatv_vidioc_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls)
+{
+#if 0 // V4L2_CTRL_CLASS_TUNER non-public linux
+       struct marutuner_dev *marutuner = video_drvdata(file);
+       struct dvb_frontend *fe = marutuner->fe;
+       int i, err = 0;
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_TUNER) {
+
+               for (i = 0; i < ctrls->count; i++) {
+                       struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+                       err = atv_tuner_set_ctrl(fe, ctrl);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
+       }
+
+       return -EINVAL;
+#endif
+       return 0;
+}
+
+/* v4l2 ioctl ops for atv tuner*/
+struct v4l2_ioctl_ops marutuner_atv_ioctl_ops = {
+       .vidioc_g_tuner         = maruatv_vidioc_g_tuner,
+       .vidioc_s_tuner         = maruatv_vidioc_s_tuner,
+       .vidioc_g_frequency     = maruatv_vidioc_g_frequency,
+       .vidioc_s_frequency     = maruatv_vidioc_s_frequency,
+       .vidioc_g_std           = maruatv_vidioc_g_std,
+       .vidioc_s_std           = maruatv_vidioc_s_std,
+       .vidioc_querycap        = maruatv_vidioc_querycap,
+       .vidioc_enum_input      = maruatv_vidioc_enum_input,
+       .vidioc_g_ext_ctrls     = maruatv_vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls     = maruatv_vidioc_s_ext_ctrls,
+};
+
+/* v4l2 ops for ATV */
+static const struct v4l2_file_operations marutuner_atv_fops = {
+       .owner = THIS_MODULE,
+       .open = marutuner_atvtuner_open,
+       .release = marutuner_atvtuner_release,
+       .unlocked_ioctl = video_ioctl2,
+};
+
+static int marutuner_atv_init(struct marutuner_dev *marutuner)
+{
+       int ret = 0;
+       struct video_device *vfd_atvtuner;
+
+       print_dbg(1, "\n");
+
+       ret = v4l2_device_register(&marutuner->pdev->dev, &marutuner->v4l2_dev);
+       if (ret) {
+               goto err_v4l2_dev_register;
+       }
+
+       vfd_atvtuner = video_device_alloc();
+       if (!vfd_atvtuner) {
+               print_err("failed to allocate video tuner device\n");
+               ret = -ENOMEM;
+               goto err_vfd_atvtuner_alloc;
+       }
+
+       vfd_atvtuner->fops = &marutuner_atv_fops;
+       vfd_atvtuner->ioctl_ops = &marutuner_atv_ioctl_ops;
+       vfd_atvtuner->release = video_device_release;
+       vfd_atvtuner->tvnorms = V4L2_STD_NTSC;  /* The target supports only NTSC */
+       //vfd_atvtuner->dev_parent = &marutuner->pdev->dev;
+       vfd_atvtuner->v4l2_dev =  &marutuner->v4l2_dev;
+       strcpy(vfd_atvtuner->name, "marutuner_atv_tuner");
+       video_set_drvdata(vfd_atvtuner, marutuner);
+       marutuner->vfd_atvtuner = vfd_atvtuner;
+
+       ret = video_register_device(vfd_atvtuner, VFL_TYPE_GRABBER, 50);
+       if (ret) {
+               v4l2_err(&marutuner->v4l2_dev, "failed to register video tuner device\n");
+               goto err_video_tuner_register;
+       }
+
+#if 0
+       /*
+        * ATV audio and ATV sif dummy initialization
+        * If need, we should make the devices.
+        */
+       ret = maruatv_audio_init();
+       if (ret)
+               goto err_atv_dummy_init;
+
+       ret = maruatv_sif_init();
+       if (ret)
+               goto err_atv_dummy_init;
+#endif
+
+       print_info("ATV tuner register success\n");
+
+       return 0;
+
+#if 0
+err_atv_dummy_init:
+       maruatv_cleanup();
+#endif
+err_video_tuner_register:
+       video_device_release(vfd_atvtuner);
+err_vfd_atvtuner_alloc:
+       v4l2_device_unregister(&marutuner->v4l2_dev);
+err_v4l2_dev_register:
+
+       return ret;
+}
+
+/*
+ * dtv frontend
+ */
+static int marutuner_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       *ber = 1;       //usually how?
+       return 0;
+}
+
+static int marutuner_fe_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+       *strength = 1;  //usually how?
+       return 0;
+}
+
+static int marutuner_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       *snr = 1;       //usually how?
+       return 0;
+}
+
+static int marutuner_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       *ucblocks = 0;  //usually how?
+       return 0;
+}
+
+static int marutuner_fe_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+       struct marutuner_dev *marutuner = state->dev;
+       unsigned int st = 0;
+       enum fe_status tmp = 0;
+
+       st = readl(marutuner->io_mem + MARUTUNER_FE_STATUS);
+       //print_dbg(1, "fe status = %d\n", st);
+
+       switch (st & 0xFF) {
+       /* platform recognizes FE_HAS_SIGNAL as NOSIGNAL */
+       case MARUTUNER_FE_TUNE_FAILED:
+       case MARUTUNER_FE_HAS_ONLY_PARAM:
+               tmp = FE_HAS_SIGNAL;
+               break;
+
+       case MARUTUNER_FE_HAS_TS:
+               tmp = FE_HAS_LOCK | FE_HAS_SYNC;
+               break;
+
+       default:
+               print_err("unknown status\n");
+               break;
+       }
+
+       *status = tmp;
+       print_dbg(1, "status = 0x%x\n", *status);
+
+       return 0;
+}
+
+/*
+ * Only needed if it actually reads something from the hardware
+ */
+static int marutuner_fe_get_frontend(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+
+       c->frequency = state->current_frequency;
+       c->modulation = state->current_modulation;
+
+       return 0;
+}
+
+static int marutuner_fe_set_frontend(struct dvb_frontend *fe)
+{
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+       struct marutuner_dev *marutuner = state->dev;
+       enum fe_status st;
+       unsigned long flags;
+#ifdef TIME_STAMP_ENABLE
+       struct timeval curr_t;
+       struct timeval diff_t;
+       struct tm dt, ct;
+#endif
+
+       print_dbg(1, "freq = %d, mod = %d\n", c->frequency, c->modulation);
+
+       if ((c->frequency < fe->ops.info.frequency_min)
+               || (c->frequency > fe->ops.info.frequency_max))
+               return -EINVAL;
+
+       marutuner_fe_read_status(fe, &st);
+       if (st & MARUTUNER_FE_HAS_TS) {
+               if (c->frequency == state->current_frequency &&
+                               c->modulation == state->current_modulation) {
+                       print_dbg(1, "already tuned, so skip\n");
+                       return 0;
+               }
+       }
+
+       /* check supported modulation. used SWITCH-CASE for extension */
+       switch (c->modulation) {
+       case QPSK:
+       case VSB_8:
+       case QAM_16:
+       case QAM_64:
+       case QAM_128:
+       case QAM_256:
+               /* Nothing to do */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       //marutuner frontend setting
+       spin_lock_irqsave(&marutuner->slock, flags);
+       //writel(1, marutuner->io_mem + MARUTUNER_FE_RESET);
+       writel(c->frequency, marutuner->io_mem + MARUTUNER_FE_FREQ);
+       writel(c->modulation, marutuner->io_mem + MARUTUNER_FE_MOD);
+       writel(1, marutuner->io_mem + MARUTUNER_FE_TUNE);
+
+       state->current_frequency = c->frequency;
+       state->current_modulation = c->modulation;
+
+#ifdef TIME_STAMP_ENABLE
+       do_gettimeofday(&curr_t);
+       diff_t.tv_sec = curr_t.tv_sec - tuned_old_t.tv_sec;
+       if (curr_t.tv_usec >= tuned_old_t.tv_usec) {
+               diff_t.tv_usec = curr_t.tv_usec - tuned_old_t.tv_usec;
+       } else {
+               diff_t.tv_sec -= 1;
+               diff_t.tv_usec = tuned_old_t.tv_usec - curr_t.tv_usec;
+       }
+       tuned_old_t = curr_t;
+       time_to_tm(diff_t.tv_sec, 0, &dt);
+       time_to_tm(curr_t.tv_sec, 0, &ct);
+       print_dbg(1, "current(%d:%d:%ld), during(%d:%d:%ld)\n",
+                       ct.tm_min, ct.tm_sec, curr_t.tv_usec,
+                       dt.tm_min, dt.tm_sec, diff_t.tv_usec);
+#endif
+       spin_unlock_irqrestore(&marutuner->slock, flags);
+
+       if (fe->ops.tuner_ops.set_params) {
+               fe->ops.tuner_ops.set_params(fe);
+               if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 0);
+       }
+
+       return 0;
+}
+
+#if 0
+static int marutuner_fe_sleep(struct dvb_frontend* fe)
+{
+       return 0;
+}
+#endif
+
+/* dvb_frontend_release doesn't call this function */
+static void marutuner_fe_release(struct dvb_frontend* fe)
+{
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+
+       kfree(state);
+
+       print_info("\n");
+}
+
+static int marutuner_fe_init(struct dvb_frontend* fe)
+{
+       struct marutuner_fe_state *state = fe->demodulator_priv;
+       struct marutuner_dev *marutuner = state->dev;
+
+       unsigned long flags;
+
+       spin_lock_irqsave(&marutuner->slock, flags);
+       writel(1, marutuner->io_mem + MARUTUNER_FE_RESET);
+       print_info("fe status = %02x\n", readl(marutuner->io_mem + MARUTUNER_FE_STATUS));
+       spin_unlock_irqrestore(&marutuner->slock, flags);
+
+       return 0;
+}
+
+static struct dvb_frontend_ops marutuner_atsc_fe_ops = {
+       .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+       .info = {
+               .name                   = "Marutuner ATSC",
+               .frequency_min          = 40000000,
+               .frequency_max          = 1002000000,
+               .frequency_stepsize     = 500,
+               .caps = FE_CAN_8VSB | FE_CAN_16VSB |
+                       FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+                       FE_CAN_QAM_256 | FE_CAN_QAM_AUTO
+       },
+
+       .release = marutuner_fe_release,
+       .init = marutuner_fe_init,
+       .set_frontend = marutuner_fe_set_frontend,
+       .get_frontend = marutuner_fe_get_frontend,
+
+       .read_status = marutuner_fe_read_status,
+       .read_ber = marutuner_fe_read_ber,
+       .read_signal_strength = marutuner_fe_read_signal_strength,
+       .read_snr = marutuner_fe_read_snr,
+       .read_ucblocks = marutuner_fe_read_ucblocks,
+};
+
+/*
+ * support dvb except for DVBC_ANNEX_C and DVBS
+ */
+static struct dvb_frontend_ops marutuner_dvb_fe_ops = {
+       .delsys = { SYS_DVBT, SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_B, SYS_DVBT2 },
+       .info = {
+               .name                   = "Marutuner DVB",
+               .frequency_min          = 4000000,
+               .frequency_max          = 1002000000,
+               .frequency_stepsize     = 500,
+               .caps = FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                       FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+                       FE_CAN_QAM_AUTO
+       },
+
+       .release = marutuner_fe_release,
+       .init = marutuner_fe_init,
+       .set_frontend = marutuner_fe_set_frontend,
+       .get_frontend = marutuner_fe_get_frontend,
+
+       .read_status = marutuner_fe_read_status,
+       .read_ber = marutuner_fe_read_ber,
+       .read_signal_strength = marutuner_fe_read_signal_strength,
+       .read_snr = marutuner_fe_read_snr,
+       .read_ucblocks = marutuner_fe_read_ucblocks,
+};
+
+static struct dvb_frontend *marutuner_fe_attach(struct marutuner_dev *marutuner)
+{
+       struct marutuner_fe_state *state = NULL;
+       int system;
+
+       /* allocate memory for the internal state */
+       state = kzalloc(sizeof(struct marutuner_fe_state), GFP_KERNEL);
+       if (!state) {
+               print_err("Can't allocate memory for marutuner frontend state\n");
+               return NULL;
+       }
+
+       /* check whether ATSC or DVB */
+       system = readl(marutuner->io_mem + MARUTUNER_FE_GET_SYSTEM);
+       print_info("dvb system : %s\n", system == 0 ? "ATSC" : "DVB");
+
+       /* determine frontend ops */
+       switch (system) {
+       case MARUTUNER_CONFIG_SYSTEM_ATSC:
+               memcpy(&state->frontend.ops, &marutuner_atsc_fe_ops, sizeof(struct dvb_frontend_ops));
+               break;
+       case MARUTUNER_CONFIG_SYSTEM_DVB:
+               memcpy(&state->frontend.ops, &marutuner_dvb_fe_ops, sizeof(struct dvb_frontend_ops));
+               break;
+       default:
+               print_err("unknown system\n");
+               kfree(state);
+               return NULL;
+       }
+
+       state->frontend.demodulator_priv = state;
+       state->dev = marutuner;
+
+       return &state->frontend;
+}
+
+/*****************************************************************************
+ * demux
+ *****************************************************************************/
+
+static inline struct marutuner_dev *feed_to_marutuner(struct dvb_demux_feed *feed)
+{
+       return container_of(feed->demux, struct marutuner_dev, demux);
+}
+
+static int marutuner_dma_map(struct marutuner_dev *marutuner)
+{
+       int ret = 0;
+
+       marutuner->ts_buf = pci_alloc_consistent(marutuner->pdev,
+                                               MARUTUNER_MEM_SIZE,
+                                               &marutuner->dma_addr);
+       if (!marutuner->ts_buf)
+               ret = -ENOMEM;
+
+       return ret;
+}
+
+static void marutuner_dma_unmap(struct marutuner_dev *marutuner)
+{
+       pci_free_consistent(marutuner->pdev,
+                       MARUTUNER_MEM_SIZE,
+                       marutuner->ts_buf,
+                       marutuner->dma_addr);
+}
+
+static void marutuner_set_dma_addr(struct marutuner_dev *marutuner)
+{
+       writel(marutuner->dma_addr, marutuner->io_mem + MARUTUNER_SETDMA);
+}
+
+static int marutuner_get_device_id(struct marutuner_dev *marutuner)
+{
+       return readl(marutuner->io_mem + MARUTUNER_FE_GET_ID);
+}
+
+static irqreturn_t marutuner_isr(int irq, void *dev_id)
+{
+       struct marutuner_dev *marutuner = dev_id;
+       unsigned int intr = 0;
+       unsigned int status = 0;
+       unsigned int count = 0;
+
+       spin_lock(&marutuner->slock);
+
+       intr = readl(marutuner->io_mem + MARUTUNER_INT);
+       if (!intr) {
+               //print_info("This irq is not for this module\n");
+               spin_unlock(&marutuner->slock);
+               return IRQ_NONE;
+       }
+
+       print_dbg(2, "This irq is mine\n");
+
+       /* TODO : error handling need? */
+       if (intr & MARUTUNER_ERR_INT) {
+               print_err("tuner device has errors(intr = %u)\n", intr);
+               spin_unlock(&marutuner->slock);
+               //increase error count
+               return IRQ_HANDLED;
+       }
+
+       count = readl(marutuner->io_mem + MARUTUNER_HWPTR);
+       print_dbg(2, "dma read count = %d\n", count);
+       if (count > MARUTUNER_MEM_SIZE) {
+               print_err("DMA size overflow\n");
+               /* TODO : handle */
+       } else if (count < MARUTUNER_MEM_SIZE) {
+               marutuner->eos = 1;
+       }
+
+#if 0
+       u8 *buf = marutuner->ts_buf;
+       print_dbg(2, "ts_buf(%p) = %02x %02x %02x %02x %02x %02x %02x %02x\n", buf,
+                               buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+#endif
+       dvb_dmx_swfilter(&marutuner->demux, marutuner->ts_buf, count);
+
+       if (dvb_ringbuffer_free(&marutuner->dmxdev.dvr_buffer) >= MARUTUNER_MIN_BUFFER_SIZE ||
+               dvb_ringbuffer_empty(&marutuner->dmxdev.dvr_buffer)) {
+               print_dbg(2, "Free space(%p) is enough(%zd or empty) %d->0 count=%d\n",
+                       &marutuner->dmxdev.dvr_buffer,
+                       dvb_ringbuffer_free(&marutuner->dmxdev.dvr_buffer),
+                       marutuner->defer_request,
+                       count);
+               /* if the dma isn't working, should not request for dma start */
+               status = readl(marutuner->io_mem + MARUTUNER_START);
+               if (status)
+                       writel(1, marutuner->io_mem + MARUTUNER_START);
+       } else {
+               if (dvb_ringbuffer_avail(&marutuner->dmxdev.dvr_buffer) > 0)
+                       marutuner->defer_request = 1;
+               print_dbg(2, "Free space(%p) is small(%zd) %d count=%d\n",
+                       &marutuner->dmxdev.dvr_buffer,
+                       dvb_ringbuffer_free(&marutuner->dmxdev.dvr_buffer),
+                       marutuner->defer_request,
+                       count);
+       }
+
+       spin_unlock(&marutuner->slock);
+
+       return IRQ_HANDLED;
+}
+
+static int marutuner_start_feed(struct dvb_demux_feed *f)
+{
+       struct marutuner_dev *marutuner = feed_to_marutuner(f);
+       unsigned long flags;
+#ifdef TIME_STAMP_ENABLE
+       struct timeval curr_t;
+       struct timeval f_diff_t;
+       struct timeval t_diff_t;
+       struct tm f_dt, t_dt, ct;
+#endif
+
+       spin_lock_irqsave(&marutuner->slock, flags);
+       switch (f->type) {
+       case DMX_TYPE_TS:
+               writel(f->pid, marutuner->io_mem + MARUTUNER_START_PID_FILTER);
+               print_dbg(1, "ts(pid=%d[0x%x]) filter started\n", f->pid, f->pid);
+               break;
+       case DMX_TYPE_SEC:
+               if (marutuner->section_users++ == 0)
+                       writel(1, marutuner->io_mem + MARUTUNER_START);
+
+#ifdef TIME_STAMP_ENABLE
+               if (f->pid == 0) {
+                       do_gettimeofday(&curr_t);
+
+                       f_diff_t.tv_sec = curr_t.tv_sec - feed_old_t.tv_sec;
+                       if (curr_t.tv_usec >= feed_old_t.tv_usec) {
+                               f_diff_t.tv_usec = curr_t.tv_usec - feed_old_t.tv_usec;
+                       } else {
+                               f_diff_t.tv_sec -= 1;
+                               f_diff_t.tv_usec = feed_old_t.tv_usec - curr_t.tv_usec;
+                       }
+                       feed_old_t = curr_t;
+
+                       t_diff_t.tv_sec = curr_t.tv_sec - tuned_old_t.tv_sec;
+                       if (curr_t.tv_usec >= tuned_old_t.tv_usec) {
+                               t_diff_t.tv_usec = curr_t.tv_usec - tuned_old_t.tv_usec;
+                       } else {
+                               t_diff_t.tv_sec -= 1;
+                               t_diff_t.tv_usec = tuned_old_t.tv_usec - curr_t.tv_usec;
+                       }
+
+                       time_to_tm(curr_t.tv_sec, 0, &ct);
+                       time_to_tm(f_diff_t.tv_sec, 0, &f_dt);
+                       time_to_tm(t_diff_t.tv_sec, 0, &t_dt);
+                       print_dbg(1, "current(%d:%d:%ld), during(%d:%d:%ld), front-feed diff(%d:%d:%ld)\n",
+                                       ct.tm_min, ct.tm_sec, curr_t.tv_usec,
+                                       f_dt.tm_min, f_dt.tm_sec, f_diff_t.tv_usec,
+                                       t_dt.tm_min, t_dt.tm_sec, t_diff_t.tv_usec);
+               }
+#endif
+
+               print_dbg(1, "section(pid=%d[0x%x]) section_users(%d)\n",
+                               f->pid, f->pid, marutuner->section_users);
+               break;
+       default:
+               print_dbg(1, "Unsupported feed type(%d)\n", f->type);
+               break;
+       }
+
+       spin_unlock_irqrestore(&marutuner->slock, flags);
+
+       return 0;
+}
+
+static int marutuner_stop_feed(struct dvb_demux_feed *f)
+{
+       struct marutuner_dev *marutuner = feed_to_marutuner(f);
+       unsigned long flags;
+
+       spin_lock_irqsave(&marutuner->slock, flags);
+       switch (f->type) {
+       case DMX_TYPE_TS:
+               writel(f->pid, marutuner->io_mem + MARUTUNER_STOP_PID_FILTER);
+               print_dbg(1, "ts(pid=%d[0x%x]) filter stopped\n", f->pid, f->pid);
+               break;
+       case DMX_TYPE_SEC:
+               if (--marutuner->section_users == 0)
+                       writel(0, marutuner->io_mem + MARUTUNER_START);
+               print_dbg(1, "section(pid=%d[0x%x]) section_users(%d)\n",
+                               f->pid, f->pid, marutuner->section_users);
+               break;
+       default:
+               print_dbg(1, "Unsupported feed type(%d)\n", f->type);
+               break;
+       }
+
+       marutuner->defer_request = 0;
+       spin_unlock_irqrestore(&marutuner->slock, flags);
+
+       return 0;
+}
+static int marutuner_hw_init(struct marutuner_dev *marutuner)
+{
+       /* clear tsfile mapping table */
+       //marutuner_reset_frontend(marutuner);
+
+       /* map DMA and set address */
+       marutuner_dma_map(marutuner);
+       marutuner_set_dma_addr(marutuner);
+
+       return 0;
+}
+
+static void marutuner_hw_exit(struct marutuner_dev *marutuner)
+{
+       marutuner_dma_unmap(marutuner);
+}
+
+static int marutuner_frontend_init(struct marutuner_dev *marutuner)
+{
+       int ret;
+
+       marutuner->fe = marutuner_fe_attach(marutuner);
+       if (!marutuner->fe) {
+               print_err("could not attach frontend\n");
+               return -ENODEV;
+       }
+
+       marutuner->fe->id = marutuner->id;
+       ret = dvb_register_frontend(marutuner->adapter, marutuner->fe);
+       if (ret < 0) {
+               if (marutuner->fe->ops.release)
+                       marutuner->fe->ops.release(marutuner->fe);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int marutuner_probe(struct pci_dev *pdev,
+                               const struct pci_device_id *id)
+{
+       struct marutuner_dev *marutuner;
+       struct dvb_demux *dvbdemux;
+       struct dmx_demux *dmx;
+       int ret = -ENOMEM;
+
+       marutuner = kzalloc(sizeof(struct marutuner_dev), GFP_KERNEL);
+       if (!marutuner) {
+               print_err("no memory\n");
+               return -ENOMEM;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret < 0) {
+               print_dbg(1, "failed pci_enable_device\n");
+               goto err_marutuner_dev_free;
+       }
+
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       if (ret < 0) {
+               print_dbg(1, "failed pci_set_dma_mask\n");
+               goto err_pci_disable_device;
+       }
+
+       pci_set_master(pdev);
+
+       ret = pci_request_region(pdev, 0, DRIVER_NAME);
+       if (ret < 0) {
+               print_dbg(1, "failed pci_request_region\n");
+               goto err_pci_disable_device;
+       }
+
+       marutuner->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+       if (!marutuner->io_mem) {
+               ret = -EIO;
+               print_dbg(1, "failed pci_iomap\n");
+               goto err_pci_release_regions;
+       }
+
+       pci_set_drvdata(pdev, marutuner);
+
+       ret = request_irq(pdev->irq, marutuner_isr, IRQF_SHARED, DRIVER_NAME, marutuner);
+       if (ret < 0) {
+               print_dbg(1, "failed request_irq\n");
+               goto err_pci_iounmap;
+       }
+
+       ret = marutuner_hw_init(marutuner);
+       if (ret < 0) {
+               print_dbg(1, "failed marutuner_hw_init\n");
+               goto err_free_irq;
+       }
+
+       /* make system-unique adapter */
+       if (!marutuner_adapter) {
+               marutuner_adapter = kzalloc(sizeof(struct dvb_adapter), GFP_KERNEL);
+               if (!marutuner_adapter) {
+                       print_err("no memory\n");
+                       goto err_marutuner_hw_exit;
+               }
+
+               /* just once register adapter */
+               ret = dvb_register_adapter(marutuner_adapter, "marutuner_dvb_adapter",
+                               THIS_MODULE, &pdev->dev, adapter_nr);
+               if (ret < 0) {
+                       print_dbg(1, "failed dvb_register_adapter\n");
+                       goto err_marutuner_adapter_free;
+               }
+       }
+
+       marutuner->adapter = marutuner_adapter;
+       marutuner->pdev = pdev;
+       marutuner->id = marutuner_get_device_id(marutuner);
+       print_info("device id = %d\n", marutuner->id);
+
+       // hold maintuner as global ptr, because vidio device should query its status
+       if (marutuner->id == 0)
+               main_tuner = marutuner;
+
+       dvbdemux = &marutuner->demux;
+       dvbdemux->filternum = 256;
+       dvbdemux->feednum = 256;
+       dvbdemux->start_feed = marutuner_start_feed;
+       dvbdemux->stop_feed = marutuner_stop_feed;
+       dvbdemux->dmx.capabilities = 0;         /* I can't find when this is used */
+
+       ret = dvb_dmx_init(dvbdemux);
+       if (ret < 0) {
+               print_dbg(1, "failed dvb_dmx_init\n");
+               goto err_dvb_unregister_adapter;
+       }
+
+       dmx = &dvbdemux->dmx;
+       marutuner->dmxdev.filternum = 256;
+       marutuner->dmxdev.demux = dmx;
+
+       /* dvb ringbuffer overflow W/A
+        * marutuner_dmxdev_xxx is W/A for dvb_ringbuffer overflow issue
+        * on old stv platforms.
+        * But, on recent platform, that issue doesn't occur any more.
+        * Nevertheless, to debug for the future problem,
+        * this statement is remained as comment.
+        */
+       //ret = marutuner_dmxdev_init(&marutuner->dmxdev, marutuner->adapter);
+       ret = dvb_dmxdev_init(&marutuner->dmxdev, marutuner->adapter);
+       if (ret < 0) {
+               print_dbg(1, "failed marutuner_dmxdev_init\n");
+               goto err_dvb_dmx_release;
+       }
+
+       marutuner->hw_frontend.source = DMX_FRONTEND_0;
+       ret = dmx->add_frontend(dmx, &marutuner->hw_frontend);
+       if (ret < 0) {
+               print_dbg(1, "failed add_frontend hw\n");
+               goto err_dvb_dmxdev_release;
+       }
+
+       marutuner->mem_frontend.source = DMX_MEMORY_FE;
+       ret = dmx->add_frontend(dmx, &marutuner->mem_frontend);
+       if (ret < 0) {
+               print_dbg(1, "failed add_frontend mem\n");
+               goto err_remove_hw_frontend;
+       }
+
+       ret = dmx->connect_frontend(dmx, &marutuner->hw_frontend);
+       if (ret < 0) {
+               print_dbg(1, "failed connect_frontend hw\n");
+               goto err_remove_mem_frontend;
+       }
+
+       ret = marutuner_frontend_init(marutuner);
+       if (ret < 0) {
+               print_dbg(1, "failed marutuner_frontend_init\n");
+               goto err_disconnect_frontend;
+       }
+
+       ret = marutuner_atv_init(marutuner);
+       if (ret < 0) {
+               print_dbg(1, "failed marutuner_atv_init\n");
+               goto err_atv_init;
+       }
+
+       print_info("successfully finished\n");
+
+       return 0;
+
+err_atv_init:
+       dmx->close(dmx);
+       if (marutuner->fe)
+               dvb_unregister_frontend(marutuner->fe);
+err_disconnect_frontend:
+       dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+       dmx->remove_frontend(dmx, &marutuner->mem_frontend);
+err_remove_hw_frontend:
+       dmx->remove_frontend(dmx, &marutuner->hw_frontend);
+err_dvb_dmxdev_release:
+       //marutuner_dmxdev_release(&marutuner->dmxdev);
+       dvb_dmxdev_release(&marutuner->dmxdev);
+err_dvb_dmx_release:
+       dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+       dvb_unregister_adapter(marutuner->adapter);
+err_marutuner_adapter_free:
+       kfree(marutuner_adapter);
+       marutuner_adapter = NULL;
+err_marutuner_hw_exit:
+       marutuner_hw_exit(marutuner);
+err_free_irq:
+       free_irq(pdev->irq, marutuner);
+err_pci_iounmap:
+       pci_iounmap(pdev, marutuner->io_mem);
+err_pci_release_regions:
+       pci_release_regions(pdev);
+err_pci_disable_device:
+       pci_disable_device(pdev);
+err_marutuner_dev_free:
+       pci_set_drvdata(pdev, NULL);
+       kfree(marutuner);
+
+       return ret;
+}
+
+static void marutuner_remove(struct pci_dev *pdev)
+{
+       struct marutuner_dev *marutuner = pci_get_drvdata(pdev);
+       struct dvb_demux *dvbdemux = &marutuner->demux;
+       struct dmx_demux *dmx = &dvbdemux->dmx;
+
+#if 0
+       maruatv_cleanup();
+#endif
+
+       dmx->close(dmx);
+       if (marutuner->fe)
+               dvb_unregister_frontend(marutuner->fe);
+       dmx->disconnect_frontend(dmx);
+       dmx->remove_frontend(dmx, &marutuner->mem_frontend);
+       dmx->remove_frontend(dmx, &marutuner->hw_frontend);
+       dvb_dmxdev_release(&marutuner->dmxdev);
+       dvb_dmx_release(dvbdemux);
+       dvb_unregister_adapter(marutuner->adapter);
+       synchronize_irq(pdev->irq);
+       free_irq(pdev->irq, marutuner);
+       pci_iounmap(pdev, marutuner->io_mem);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+       kfree(marutuner);
+       kfree(marutuner_adapter);
+       marutuner_adapter = NULL;
+}
+
+static struct pci_device_id marutuner_id_table[] = {
+       {
+               .vendor = PCI_VENDOR_ID_TIZEN,
+               .device = PCI_DEVICE_ID_VIRTUAL_TUNER,
+               .subvendor = PCI_ANY_ID,
+               .subdevice = PCI_ANY_ID,
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(pci, marutuner_id_table);
+
+static struct pci_driver marutuner_driver = {
+       .name           = MARUTUNER_MODULE_NAME,
+       .id_table       = marutuner_id_table,
+       .probe          = marutuner_probe,
+       .remove         = marutuner_remove,
+};
+
+static int __init marutuner_init(void)
+{
+       int retv = 0;
+
+       retv = pci_register_driver(&marutuner_driver);
+       if (retv < 0)
+               print_err("module loading fail");
+
+       return retv;
+}
+
+static void __exit marutuner_exit(void)
+{
+       pci_unregister_driver(&marutuner_driver);
+}
+
+module_init(marutuner_init);
+module_exit(marutuner_exit);
+
+MODULE_DESCRIPTION("MARU Virtual Tuner Driver");
+MODULE_AUTHOR("Byeongki Shin <bk0121.shin@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/maru/tv/maru_tuner.h b/drivers/maru/tv/maru_tuner.h
new file mode 100644 (file)
index 0000000..eb152fc
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * MARU Virtual Tuner Driver Header
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ * Byeongki Shin <bk0121.shin@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
+ *
+ */
+
+#ifndef __MARU_TUNER_H
+#define __MARU_TUNER_H
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <media/v4l2-device.h>
+
+#include "../drivers/media/dvb-core/demux.h"
+#include "../drivers/media/dvb-core/dmxdev.h"
+#include "../drivers/media/dvb-core/dvb_demux.h"
+#include "../drivers/media/dvb-core/dvb_frontend.h"
+#include "../drivers/media/dvb-core/dvbdev.h"
+
+#define MARUTUNER_MODULE_NAME  "marutuner"
+#define DRIVER_NAME            "marutuner"
+#define MARUTUNER_TS_PACKETS   1024
+//#define MARUTUNER_MEM_SIZE   (188 * MARUTUNER_TS_PACKETS)    /* size of a TS packet is 188 byte */
+#define MARUTUNER_MEM_SIZE     0x2000
+#define MARUTUNER_MIN_BUFFER_SIZE      ((((MARUTUNER_MEM_SIZE - 1) / 188) + 1) * 188)
+
+/*
+ * PCI register definition
+ * kernel and qemu must be synchronized.
+ */
+enum marutuner_cmd_reg {
+    MARUTUNER_FE_RESET         = 0x00,
+    MARUTUNER_FE_STATUS        = 0x04,
+    MARUTUNER_FE_FREQ          = 0x08,
+    MARUTUNER_FE_MOD           = 0x0c,
+    MARUTUNER_FE_TUNE          = 0x10,
+    MARUTUNER_FE_GET_SYSTEM    = 0x14,
+    MARUTUNER_FE_GET_ID                = 0x18,
+
+    MARUTUNER_SETDMA           = 0x20,
+    MARUTUNER_START            = 0x24,
+    MARUTUNER_INT              = 0x28,
+    MARUTUNER_HWPTR            = 0x2c,
+    MARUTUNER_START_PID_FILTER = 0x30,
+    MARUTUNER_STOP_PID_FILTER  = 0x34,
+
+    MARUTUNER_ATV_SET_TUNE     = 0x38,
+    MARUTUNER_ATV_GET_STATUS   = 0x3c,
+};
+
+/* frontend system */
+#define MARUTUNER_CONFIG_SYSTEM_ATSC   0
+#define MARUTUNER_CONFIG_SYSTEM_DVB    1
+
+/* frontend status */
+#define        MARUTUNER_FE_TUNE_FAILED        0x00
+#define MARUTUNER_FE_HAS_ONLY_PARAM    0x01
+#define        MARUTUNER_FE_HAS_TS             0x02
+
+/* demux interrupt */
+#define MARUTUNER_DMA_COMPLETE_INT     0x01
+#define MARUTUNER_ERR_INT              0x08
+
+struct marutuner_fe_state {
+       struct dvb_frontend frontend;
+       struct marutuner_dev *dev;
+
+       u32 current_frequency;
+       enum fe_modulation current_modulation;
+};
+
+struct marutuner_dev {
+       /* pci */
+       struct pci_dev *pdev;
+       //resource_size_t io_base;
+       //resource_size_t io_size;
+       void __iomem *io_mem;
+       //resource_size_t mem_base;     /* virtual dma address*/
+       //resource_size_t mem_size;
+       //void __iomem *data_mem;
+       int id;
+
+       /* dvb */
+       struct dmx_frontend hw_frontend;
+       struct dmx_frontend mem_frontend;
+       struct dmxdev dmxdev;
+       struct dvb_adapter *adapter;
+       struct dvb_demux demux;
+       struct dvb_frontend *fe;
+       //struct dvb_net dvbnet;
+       unsigned int section_users;
+       int defer_request;
+       spinlock_t  slock;
+
+       /* atv */
+       struct v4l2_device v4l2_dev;
+       struct video_device *vfd_atvtuner;
+#if 0
+       struct video_device *vfd_adc;
+       struct video_device *vfd_audio;
+       struct video_device *vfd_sif;
+#endif
+
+       /* irq */
+       unsigned int eos;               /* end-of-stream */
+
+       /* dma */
+       dma_addr_t dma_addr;
+       unsigned char *ts_buf;
+};
+
+
+int marutuner_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dmx_adapter);
+void marutuner_dmxdev_release(struct dmxdev *dmxdev);
+int maruatv_get_status(void);
+
+#endif /* #ifndef __MARU_TUNER_H */
diff --git a/drivers/maru/tv/maru_v4l2.h b/drivers/maru/tv/maru_v4l2.h
new file mode 100644 (file)
index 0000000..0a57fd7
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Maru v4l2 header file
+ *
+ * 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
+ *
+ */
+
+enum v4l2_drm_inputport_type {
+       V4L2_DRM_INPUTPORT_TYPE_DTV = 0,
+       V4L2_DRM_INPUTPORT_TYPE_MM,
+       V4L2_DRM_INPUTPORT_TYPE_ATV,
+       V4L2_DRM_INPUTPORT_TYPE_AV,
+       V4L2_DRM_INPUTPORT_TYPE_COMPONENT,
+       V4L2_DRM_INPUTPORT_TYPE_RGB,
+       V4L2_DRM_INPUTPORT_TYPE_DVI,
+       V4L2_DRM_INPUTPORT_TYPE_HDMI,
+       V4L2_DRM_INPUTPORT_TYPE_MAIN_CLONE,     // this is not V4L2 but... just put it here for convenience
+       V4L2_DRM_INPUTPORT_TYPE_JPEG_SWDEC,
+       V4L2_DRM_INPUTPORT_TYPE_MJPEG,
+       V4L2_DRM_INPUTPORT_TYPE_SCART,
+       V4L2_DRM_INPUTPORT_TYPE_DSP,  // add for vdsp
+       V4L2_DRM_INPUTPORT_TYPE_LVDXRX,//added for SBB
+       V4L2_DRM_INPUTPORT_TYPE_MAX
+};
+
+enum v4l2_aspectratio_type {
+       V4L2_ASPECTRATIO_TYPE_100_100   = 1, // 1:1
+       V4L2_ASPECTRATIO_TYPE_300_400   = 2, // 3:4
+       V4L2_ASPECTRATIO_TYPE_900_1600  = 3, // 9:16
+       V4L2_ASPECTRATIO_TYPE_100_221   = 4, // 1:2.21
+};
+
+/* Source color format */
+enum v4l2_drm_colorformat_type {
+       V4L2_DRM_COLORFORMAT_RGB444 = 1,        /**< RGB 444 */
+       V4L2_DRM_COLORFORMAT_YUV444 = 2,        /**< YUV 444 */
+       V4L2_DRM_COLORFORMAT_YUV422 = 3,        /**< YUV 422 */
+       V4L2_DRM_COLORFORMAT_YUV420 = 4,        /**< YUV 420 */
+       V4L2_DRM_COLORFORMAT_YC         = 5,    /**< YC */
+};
+
+/** Frame rate definition */
+enum v4l2_drm_framerate_type {
+       /*! 23.97Hz frame rate */
+       V4L2_DRM_FRAMERATE_23_97HZ = 1, /* Video Clock 74.25MHz, if 74.175MHz =>23.97Hz */
+       /*! 24Hz frame rate */
+       V4L2_DRM_FRAMERATE_24HZ,                /* Video Clock 74.25MHz, if 74.175MHz =>23.97Hz */
+       /*! 25Hz frame rate */
+       V4L2_DRM_FRAMERATE_25HZ,
+       /*! 29.97Hz frame rate */
+       V4L2_DRM_FRAMERATE_29_97HZ,
+       /*! 30Hz frame rate */
+       V4L2_DRM_FRAMERATE_30HZ,                /* Video Clock 74.25MHz, if 74.175MHz =>29.97Hz */
+       /*! 50Hz frame rate */
+       V4L2_DRM_FRAMERATE_50HZ,
+       /*! 59.94Hz frame rate */
+       V4L2_DRM_FRAMERATE_59_94HZ,
+       /*! 60Hz frame rate */
+       V4L2_DRM_FRAMERATE_60HZ,                /* Video Clock 74.25MHz, if 74.175MHz =>59.94Hz */
+       /*! 60Hz frame rate */
+       V4L2_DRM_FRAMERATE_48HZ,                /* Video Clock 74.25MHz, if 74.175MHz =>59.94Hz */
+       /*! 47.94Hz frame rate */
+       V4L2_DRM_FRAMERATE__47_94HZ,
+       /*! 75Hz frame rate */
+       V4L2_DRM_FRAMERATE_75HZ,
+       /*! 100Hz frame rate */
+       V4L2_DRM_FRAMERATE_100HZ,
+       /*! 120Hz frame rate */
+       V4L2_DRM_FRAMERATE_120HZ,
+       /*! 120Hz frame rate */
+       V4L2_DRM_FRAMERATE_ETC
+};
+
+enum v4l2_drm_input_stereoscopic_type
+{
+       V4L2_DRM_INPUT_STEREOSCOPIC_2D,
+       V4L2_DRM_INPUT_STEREOSCOPIC_SBS,
+       V4L2_DRM_INPUT_STEREOSCOPIC_TNB,
+       V4L2_DRM_INPUT_STEREOSCOPIC_FRAME_SEQ,
+       V4L2_DRM_INPUT_STEREOSCOPIC_FRAME_PACK,
+       V4L2_DRM_INPUT_STEREOSCOPIC_3D_KR3D,
+       V4L2_DRM_INPUT_STEREOSCOPIC_3D_MVC_B,
+       V4L2_DRM_INPUT_STEREOSCOPIC_3D_MFC_D,
+       V4L2_DRM_INPUT_STEREOSCOPIC_3D_SVAF,
+};
+
+enum v4l2_vbi_line_std
+{
+       V4L2_VBI_MAN_LINE_AUTO                  = 0,
+       V4L2_VBI_MAN_LINE_TTXT                  = 1,
+       V4L2_VBI_MAN_LINE_VP                    = 2,
+       V4L2_VBI_MAN_LINE_VI                    = 3,
+       V4L2_VBI_MAN_LINE_CGMS                  = 4,
+       V4L2_VBI_MAN_LINE_GEMSTAR_1X            = 5,
+       V4L2_VBI_MAN_LINE_GEMSTAR_2X            = 6,
+       V4L2_VBI_MAN_LINE_CLOSED_CAPTION                = 7,
+       V4L2_VBI_MAN_LINE_RESERVED1                             = 8,
+       V4L2_VBI_MAN_LINE_RESERVED2                             = 9,
+       V4L2_VBI_MAN_LINE_RESERVED3                             = 10,
+       V4L2_VBI_MAN_LINE_RESERVED4                             = 11,
+       V4L2_VBI_MAN_LINE_RESERVED5                             = 12,
+       V4L2_VBI_MAN_LINE_CUSTOM1                               = 13,
+       V4L2_VBI_MAN_LINE_CUSTOM2                               = 14,
+       V4L2_VBI_MAN_LINE_DISABLE                               = 15,
+
+       V4L2_VBI_MAN_LINE_MAX
+};
+
+struct v4l2_private_frame_info
+{
+       __u32 chip_id;
+       __u32 instance_id;
+       __u32 display_index;
+       __u32 showframe;
+       __u32 y_viraddr;
+       __u32 u_viraddr;
+       __u32 v_viraddr;        // added
+       __u32 width;            // source h resolution
+       __u32 height;           // source v resolution
+       __u32 y_linesize;       // width + padded bytes size,   //modified (linesize -> y_linesize)
+       __u32 u_linesize;       // width + padded bytes size  //added
+       __u32 v_linesize;       // width + padded bytes size  //added
+       __u32 startline;        // only for picture   //added
+       __u32 endline;          // only for picture   //added
+       enum v4l2_drm_colorformat_type colorformat;     /**< this is for JPEG/MJPEG */
+       __u32 framedone;
+       __u32 Keyframe;
+       __u32 dimension;        // 0: 2D, 1: 3D_Left, 2: 3D_Right
+       __u32 y_phyaddr;
+       __u32 u_phyaddr;
+       __u32 v_phyaddr;
+};
+
+struct v4l2_drm_dec_info {
+       enum v4l2_aspectratio_type aspectratio; /**< enum v4l2_aspectratio_type */
+       __u32 hres;                     /**< Source horizontal size */
+       __u32 vres;                     /**< Source vertical size */
+       __u32 framerate;        /**< Source frame rate (hz*100) : ex) 23.974Hz -> 23974, 60Hz -> 60000 */
+       __u32 scantype;         /**< Source scan type, 0 : interaced, 1 : progressive */
+       struct v4l2_private_frame_info pFrame[4];
+};
+
+struct v4l2_drm_extin_info{
+       __u32 src_info_send_flag ;  /**< flag for using ADC information 0:not use, 1:use */// TODO:  need this?
+       __u32 htotal;
+       __u32 vtotal;
+       __u32 hactive;  // h_active_start position;
+       __u32 vactive;  // v_active_start position;
+       __u32 hactive_size;
+       __u32 vactive_size;
+       __u32 scantype;         /**< Source scan type, 0 : interaced, 1 : progressive */
+       __u32 oversampling;     /**< Over Sampling 0:no over sampling, 1:over sampling */
+       enum v4l2_drm_framerate_type framerate;         /**< Source color format */
+       enum v4l2_drm_colorformat_type colorformat;     /**< Source color format */
+       __u32 syncmode; /**< DE / SYNC mode information 0:DE, 1:SYNC */
+       __u32 pcinput;  /**< PC Input 0:no PC, 1:PC format */
+       __u32 ntscpal_type;     /**< When u32SrcInfoSendFlag is '0', NTSC / PAL Type information 0:NTSC, 1:PAL */
+};
+
+struct v4l2_drm {
+       enum v4l2_drm_inputport_type inputport;   // this should be filled by driver
+       __u32 plane;    // 0 : main 1: sub , this should be fillled by application
+       union {
+               struct v4l2_drm_dec_info        dec_info;       // DTV/MM info structure
+               struct v4l2_drm_extin_info      extin_info;     // Ext.input info structure
+       }u;
+       enum v4l2_drm_input_stereoscopic_type   stereoscopic_info; // this should be fillled by application
+       __u32   reserved; // this should be filled by driver with fixed value : 0x1234ABCD
+};
+
+struct v4l2_fake_res {
+       __u32 flag;     //0:false    1:true
+       __u32 analog_fake_res;  //1:RESOLUTION_480I    0:RESOLUTION_576I
+       __u32 reserved[8];
+};
+
+struct v4l2_vbi_lines {
+       __u16 linestd;
+       __u16 startline;
+       __u16 endline;
+};
+
+#define V4L2_CID_SYNC_LOCK                      (V4L2_CID_BASE+44)
+#define V4L2_CID_MACROVISION                    (V4L2_CID_BASE+45)
+#define V4L2_CID_CGMS                           (V4L2_CID_BASE+46)
+#define V4L2_CID_NOISE                          (V4L2_CID_BASE+47)
+#define V4L2_CID_ALLOW_3DCOMB                   (V4L2_CID_BASE+48)
+#define V4L2_CID_50HZ                           (V4L2_CID_BASE+49)
+#define V4L2_CID_STANDARD                       (V4L2_CID_BASE+50)
+#define V4L2_CID_SCART                          (V4L2_CID_BASE+51)
+#define V4L2_CID_DETECT_BURST                   (V4L2_CID_BASE+52)
+#define V4L2_CID_AUTO_PRGM_LOCK                 (V4L2_CID_BASE+53)
+#define V4L2_CID_PAL_SWING                      (V4L2_CID_BASE+54)
+#define V4L2_CID_QPI_STATUS                     (V4L2_CID_BASE+55)
+
+/*TV Standards*/
+#define V4L2_SYS_TYPE_0     V4L2_STD_NTSC           //NT358 : KOR, USA, TAIWAN
+#define V4L2_SYS_TYPE_1     V4L2_STD_MN             //PAL_M,PAL_N,NT358 : BRA
+#define V4L2_SYS_TYPE_2        (V4L2_STD_SECAM      |\
+               V4L2_STD_PAL        |\
+               V4L2_STD_NTSC_443)          //PAL,SECAM,NT443 : PANEURO, PANNORDIG, S.PACIFIC, HKG
+#define V4L2_SYS_TYPE_3     (V4L2_STD_SECAM     |\
+               V4L2_STD_PAL       |\
+               V4L2_STD_NTSC_443  |\
+               V4L2_STD_NTSC)         //PAL,SECAM,NT443,NT358 : CHINA, ARB, S.ASIA, AsiaATV, C.ASIA
+
index ccc1f43cb9a9b11767c0eee35aa323f908fee370..dde93d01670d31ad9690633c1d63bf0e53bef2c7 100644 (file)
@@ -582,10 +582,10 @@ struct dmx_demux {
        int (*get_pes_pids)(struct dmx_demux *demux, u16 *pids);
 
        /* private: Not used upstream and never documented */
-#if 0
+
        int (*get_caps)(struct dmx_demux *demux, struct dmx_caps *caps);
        int (*set_source)(struct dmx_demux *demux, const dmx_source_t *src);
-#endif
+
        /*
         * private: Only used at av7110, to read some data from firmware.
         *      As this was never documented, we have no clue about what's
index 4d0dfc6222382d8d4b9c24ec8842478ebe89eda2..6c771445a19d66c47621c373b7f70b803813d048 100644 (file)
 
 #define PCI_VENDOR_ID_SAMSUNG          0x144d
 
+#define PCI_DEVICE_ID_VIRTUAL_TUNER            0x1044
+#define PCI_DEVICE_ID_VIRTUAL_DTV_DECODER      0x1048
+#define PCI_DEVICE_ID_VIRTUAL_DTV_AUDIO                0x104C
+
 #define PCI_VENDOR_ID_GIGABYTE         0x1458
 
 #define PCI_VENDOR_ID_AMBIT            0x1468