#
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
# 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
# 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
# Media ancillary drivers (tuners, sensors, i2c, frontends)
#
CONFIG_MEDIA_SUBDRV_AUTOSELECT=y
+CONFIG_MEDIA_ATTACH=y
#
# Audio decoders, processors and mixers
#
# 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
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
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
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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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],
+ ¶->filter.mask[1], DMX_FILTER_SIZE - 1);
+ memcpy(&(*secfilter)->filter_mode[3],
+ ¶->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);
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
+
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
#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