media: repeater: init version
authorshinwon.lee <shinwon.lee@samsung.com>
Fri, 15 Jun 2018 05:17:49 +0000 (14:17 +0900)
committerJanghyuck Kim <janghyuck.kim@samsung.com>
Mon, 23 Jul 2018 05:39:10 +0000 (14:39 +0900)
Change-Id: I64a3967087873f5fbc48b2d145d1648b31f4d03c
Signed-off-by: shinwon.lee <shinwon.lee@samsung.com>
drivers/media/platform/exynos/repeater/Kconfig [new file with mode: 0644]
drivers/media/platform/exynos/repeater/Makefile [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater.h [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater_buf.c [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater_buf.h [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater_dbg.h [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater_dev.c [new file with mode: 0644]
drivers/media/platform/exynos/repeater/repeater_dev.h [new file with mode: 0644]

diff --git a/drivers/media/platform/exynos/repeater/Kconfig b/drivers/media/platform/exynos/repeater/Kconfig
new file mode 100644 (file)
index 0000000..da86f09
--- /dev/null
@@ -0,0 +1,8 @@
+config VIDEO_EXYNOS_REPEATER
+       bool "EXYNOS REPEATER Driver"
+       depends on VIDEO_EXYNOS
+       default n
+       help
+         This is a platform driver for EXYNOS REPEATER device.
+       ---help---
+         Repeater driver for WFD
\ No newline at end of file
diff --git a/drivers/media/platform/exynos/repeater/Makefile b/drivers/media/platform/exynos/repeater/Makefile
new file mode 100644 (file)
index 0000000..13050c4
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Copyright (c) 2016 Samsung Electronics Co., Ltd.
+#              http://www.samsung.com
+#
+# Licensed under GPLv2
+#
+
+repeater-objs := repeater_dev.o repeater_buf.o
+obj-$(CONFIG_VIDEO_EXYNOS_REPEATER)       += repeater.o
diff --git a/drivers/media/platform/exynos/repeater/repeater.h b/drivers/media/platform/exynos/repeater/repeater.h
new file mode 100644 (file)
index 0000000..5e45ba8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * Header file for Exynos REPEATER driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REPEATER_H_
+#define _REPEATER_H_
+
+#define MAX_SHARED_BUFFER_NUM          3
+
+struct repeater_info {
+       int pixel_format;
+       int width;
+       int height;
+       int buffer_count;
+       int fps;
+       int buf_fd[MAX_SHARED_BUFFER_NUM];
+};
+
+#define REPEATER_IOCTL_MAGIC           'R'
+
+#define REPEATER_IOCTL_MAP_BUF         \
+       _IOWR(REPEATER_IOCTL_MAGIC, 0x10, struct repeater_info)
+#define REPEATER_IOCTL_UNMAP_BUF       \
+       _IO(REPEATER_IOCTL_MAGIC, 0x11)
+
+#define REPEATER_IOCTL_START           \
+       _IO(REPEATER_IOCTL_MAGIC, 0x20)
+#define REPEATER_IOCTL_STOP            \
+       _IO(REPEATER_IOCTL_MAGIC, 0x21)
+#define REPEATER_IOCTL_PAUSE   \
+       _IO(REPEATER_IOCTL_MAGIC, 0x22)
+#define REPEATER_IOCTL_RESUME  \
+       _IO(REPEATER_IOCTL_MAGIC, 0x23)
+
+#define REPEATER_IOCTL_DUMP            \
+       _IOR(REPEATER_IOCTL_MAGIC, 0x31, int)
+
+#endif /* _REPEATER_H_ */
diff --git a/drivers/media/platform/exynos/repeater/repeater_buf.c b/drivers/media/platform/exynos/repeater/repeater_buf.c
new file mode 100644 (file)
index 0000000..0e530d8
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for Exynos REPEATER driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/jiffies.h>
+
+#include "repeater_dbg.h"
+#include "repeater_buf.h"
+
+void init_shared_buffer(struct shared_buffer *bufs, int buffer_count)
+{
+       int i = 0;
+
+       bufs->buffer_count = buffer_count;
+       for (i = 0; i < bufs->buffer_count; i++) {
+               bufs->captured_timestamp_us[i] = 0;
+               bufs->buf_status[i] = SHARED_BUF_INIT;
+       }
+}
+
+int get_capturing_buf_idx(struct shared_buffer *bufs, int *buf_idx)
+{
+       int i = 0;
+       int ret = -ENOBUFS;
+       uint64_t oldest_time_us = 0xFFFFFFFFFFFFFFFF;
+       int captured_buf_index = -1;
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s++\n", __func__);
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "bufs->buf_status[%d] %d, time %lld\n",
+                       i, bufs->buf_status[i], bufs->captured_timestamp_us[i]);
+       }
+
+       // find init buf
+       for (i = 0; i < bufs->buffer_count; i++) {
+               if (bufs->buf_status[i] == SHARED_BUF_INIT) {
+                       bufs->buf_status[i] = SHARED_BUF_CAPTURING;
+                       *buf_idx = i;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       // find oldest captured buf
+       if (ret) {
+               for (i = 0; i < bufs->buffer_count; i++) {
+                       if (bufs->buf_status[i] == SHARED_BUF_CAPTURED) {
+                               oldest_time_us = bufs->captured_timestamp_us[i];
+                               captured_buf_index = i;
+                               break;
+                       }
+               }
+
+               for (i = 0; i < bufs->buffer_count; i++) {
+                       if (bufs->buf_status[i] == SHARED_BUF_CAPTURED) {
+                               if (bufs->captured_timestamp_us[i] == 0) {
+                                       oldest_time_us = bufs->captured_timestamp_us[i];
+                                       captured_buf_index = i;
+                                       break;
+                               }
+
+                               if (oldest_time_us > bufs->captured_timestamp_us[i]) {
+                                       oldest_time_us = bufs->captured_timestamp_us[i];
+                                       captured_buf_index = i;
+                               }
+                       }
+               }
+               if (captured_buf_index >= 0) {
+                       bufs->buf_status[captured_buf_index] = SHARED_BUF_CAPTURING;
+                       *buf_idx = captured_buf_index;
+                       ret = 0;
+               }
+       }
+
+       print_repeater_debug(RPT_SHR_BUF_INFO,
+               "ret %d, buf_idx %d, oldest_time_us %lld\n",
+               ret, *buf_idx, oldest_time_us);
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int set_captured_buf_idx(struct shared_buffer *bufs, int buf_idx, int cap_idx)
+{
+       int i = 0;
+       int ret = -ENOBUFS;
+       ktime_t cur_ktime;
+       uint64_t cur_timestamp;
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s++\n", __func__);
+
+       if ((buf_idx >= 0 && buf_idx < MAX_SHARED_BUF_NUM) &&
+                       bufs->buf_status[buf_idx] == SHARED_BUF_CAPTURING) {
+               if (buf_idx == cap_idx) {
+                       bufs->buf_status[buf_idx] = SHARED_BUF_CAPTURED;
+               } else {
+                       bufs->buf_status[buf_idx] = SHARED_BUF_INIT;
+                       print_repeater_debug(RPT_ERROR, "blending error, buf_idx %d, cap_idx %d\n",
+                               buf_idx, cap_idx);
+               }
+               cur_ktime = ktime_get();
+               cur_timestamp = ktime_to_us(cur_ktime);
+               bufs->captured_timestamp_us[buf_idx] = cur_timestamp;
+               ret = 0;
+
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "buf_idx %d, captured_timestamp_us %lld, cur_timestamp %lld\n",
+                       buf_idx, bufs->captured_timestamp_us[buf_idx], cur_timestamp);
+       }
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "bufs->buf_status[%d] %d, captured_timestamp_us %lld\n",
+                       i, bufs->buf_status[i], bufs->captured_timestamp_us[i]);
+       }
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+
+int get_latest_captured_buf_idx(struct shared_buffer *bufs, int *buf_idx)
+{
+       int i = 0;
+       int ret = -ENOBUFS;
+       uint64_t latest_time_us = 0;
+       int captured_buf_index = -1;
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s++\n", __func__);
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               if (bufs->buf_status[i] == SHARED_BUF_CAPTURED &&
+                       latest_time_us < bufs->captured_timestamp_us[i]) {
+                       latest_time_us = bufs->captured_timestamp_us[i];
+                       captured_buf_index = i;
+               }
+
+               if (bufs->buf_status[i] == SHARED_BUF_ENCODE) {
+                       /* MFC encoding is not done */
+                       captured_buf_index = -1;
+                       break;
+               }
+       }
+
+       if (captured_buf_index >= 0) {
+               *buf_idx = captured_buf_index;
+               ret = 0;
+       }
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "bufs->buf_status[%d] %d, time %lld\n",
+                       i, bufs->buf_status[i], bufs->captured_timestamp_us[i]);
+       }
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int set_encoding_start(struct shared_buffer *bufs, int buf_idx)
+{
+       int i = 0;
+       int ret = -ENOBUFS;
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s++\n", __func__);
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "bufs->buf_status[%d] %d",
+               buf_idx, bufs->buf_status[buf_idx]);
+
+       if (bufs->buf_status[buf_idx] == SHARED_BUF_CAPTURED) {
+               bufs->buf_status[buf_idx] = SHARED_BUF_ENCODE;
+               ret = 0;
+       }
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "bufs->buf_status[%d] %d, time %lld\n",
+                       i, bufs->buf_status[i], bufs->captured_timestamp_us[i]);
+       }
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int set_encoding_done(struct shared_buffer *bufs)
+{
+       int i = 0;
+       int ret = -ENOBUFS;
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s++\n", __func__);
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               if (bufs->buf_status[i] == SHARED_BUF_ENCODE) {
+                       bufs->buf_status[i] = SHARED_BUF_CAPTURED;
+                       ret = 0;
+               }
+       }
+
+       for (i = 0; i < bufs->buffer_count; i++) {
+               print_repeater_debug(RPT_SHR_BUF_INFO,
+                       "bufs->buf_status[%d] %d, time %lld\n",
+                       i, bufs->buf_status[i], bufs->captured_timestamp_us[i]);
+       }
+
+       print_repeater_debug(RPT_SHR_BUF_INFO, "%s--\n", __func__);
+
+       return ret;
+}
diff --git a/drivers/media/platform/exynos/repeater/repeater_buf.h b/drivers/media/platform/exynos/repeater/repeater_buf.h
new file mode 100644 (file)
index 0000000..5271417
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for Exynos REPEATER driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REPEATER_BUF_H_
+#define _REPEATER_BUF_H_
+
+#include <linux/dma-buf.h>
+
+#include <media/exynos_repeater.h>
+
+enum shared_buffer_status {
+       SHARED_BUF_INIT,
+       SHARED_BUF_CAPTURING,
+       SHARED_BUF_CAPTURED,
+       SHARED_BUF_ENCODE
+};
+
+/**
+ * struct shared_buffer
+ *
+ * @buf_status         : status of bufs
+ * @dmabuf             : dmabuf of bufs
+ * @buf_spinlock       : spinlock for bufs
+ */
+struct shared_buffer {
+       enum shared_buffer_status buf_status[MAX_SHARED_BUF_NUM];
+       uint64_t captured_timestamp_us[MAX_SHARED_BUF_NUM];
+       int buffer_count;
+};
+
+void init_shared_buffer(struct shared_buffer *bufs, int buffer_count);
+
+int get_capturing_buf_idx(struct shared_buffer *bufs, int *buf_idx);
+
+int set_captured_buf_idx(struct shared_buffer *bufs, int buf_idx, int cap_idx);
+
+int get_latest_captured_buf_idx(struct shared_buffer *bufs, int *buf_idx);
+
+int set_encoding_start(struct shared_buffer *bufs, int buf_idx);
+
+int set_encoding_done(struct shared_buffer *bufs);
+
+#endif /* _REPEATER_BUF_H_ */
diff --git a/drivers/media/platform/exynos/repeater/repeater_dbg.h b/drivers/media/platform/exynos/repeater/repeater_dbg.h
new file mode 100644 (file)
index 0000000..c5923df
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for Exynos REPEATER driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REPEATER_DBG_H_
+#define _REPEATER_DBG_H_
+
+extern int g_repeater_debug_level;
+
+#define RPT_SHR_BUF_INFO               3
+#define RPT_INT_INFO                   2
+#define RPT_EXT_INFO                   1
+#define RPT_ERROR                              0
+
+#define print_repeater_debug(level, fmt, args...)              \
+       do {                                                    \
+               if (g_repeater_debug_level >= level)            \
+                       pr_info("%s:%d: " fmt,        \
+                               __func__, __LINE__, ##args);    \
+       } while (0)
+
+#endif
diff --git a/drivers/media/platform/exynos/repeater/repeater_dev.c b/drivers/media/platform/exynos/repeater/repeater_dev.c
new file mode 100644 (file)
index 0000000..cd53b37
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+ * copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Core file for Samsung EXYNOS TSMUX driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/uaccess.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/ion.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+
+#include "repeater_dev.h"
+#include "repeater_dbg.h"
+
+#define REPEATER_CUR_CONTEXTS_NUM              0
+
+static struct repeater_device *g_repeater_device;
+static spinlock_t repeater_spinlock;
+
+#define ENCODING_PERIOD_TIME_US                        1000000
+#define MAX_WAIT_TIME_DUMP                             10000000
+
+int g_repeater_debug_level;
+module_param(g_repeater_debug_level, int, 0600);
+
+/*
+ * If fps is 60, MFC encoding is called when hwfc_set_valid_buffer is called
+ */
+
+int hwfc_request_buffer(struct shared_buffer_info *info, int owner)
+{
+       int ret = 0;
+       struct repeater_context *ctx;
+       int i = 0;
+       int *buf_fd;
+       int idx = REPEATER_CUR_CONTEXTS_NUM;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_EXT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (!g_repeater_device || !info) {
+               print_repeater_debug(RPT_ERROR, "%s, g_repeater %p, info %p\n",
+                       __func__, g_repeater_device, info);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       ctx = g_repeater_device->ctx[idx];
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx %p\n", __func__, ctx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       if (ctx->ctx_status == REPEATER_CTX_INIT) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       info->pixel_format = ctx->info.pixel_format;
+       info->width = ctx->info.width;
+       info->height = ctx->info.height;
+       info->buffer_count = ctx->info.buffer_count;
+       buf_fd = ctx->info.buf_fd;
+
+       for (i = 0; i < info->buffer_count; i++) {
+               get_dma_buf(ctx->dmabufs[i]);
+               info->bufs[i] = ctx->dmabufs[i];
+       }
+
+       print_repeater_debug(RPT_EXT_INFO,
+               "f 0x%x, w %d, h %d, buf_cnt %d\n", info->pixel_format,
+               info->width, info->height, info->buffer_count);
+       for (i = 0; i < info->buffer_count; i++)
+               print_repeater_debug(RPT_EXT_INFO,
+                       "owner %d, dma_buf[%d] %p\n", owner, i, info->bufs[i]);
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_EXT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int hwfc_get_valid_buffer(int *buf_idx)
+{
+       int ret = 0;
+       struct repeater_context *ctx;
+       struct shared_buffer *shr_bufs;
+       int idx = REPEATER_CUR_CONTEXTS_NUM;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_EXT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (!g_repeater_device || !buf_idx) {
+               print_repeater_debug(RPT_ERROR, "%s, g_repeater %p, buf_idx %p\n",
+                       __func__, g_repeater_device, buf_idx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       ctx = g_repeater_device->ctx[idx];
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx %p\n", __func__, ctx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       if (ctx->ctx_status == REPEATER_CTX_INIT) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       shr_bufs = &ctx->shared_bufs;
+       ret = get_capturing_buf_idx(shr_bufs, buf_idx);
+
+       print_repeater_debug(RPT_EXT_INFO, "ret %d, buf_idx %d\n", ret, *buf_idx);
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_EXT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int hwfc_set_valid_buffer(int buf_idx, int capture_idx)
+{
+       int ret = 0;
+       struct repeater_context *ctx;
+       struct shared_buffer *shr_bufs;
+       int idx = REPEATER_CUR_CONTEXTS_NUM;
+       unsigned long flags;
+       ktime_t cur_ktime;
+       uint64_t cur_timestamp;
+
+       print_repeater_debug(RPT_EXT_INFO, "%s++\n", __func__);
+
+       print_repeater_debug(RPT_EXT_INFO, "buf_idx %d, capture_idx %d\n",
+               buf_idx, capture_idx);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (!g_repeater_device) {
+               print_repeater_debug(RPT_ERROR, "%s, g_repeater %p\n",
+                       __func__, g_repeater_device);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       ctx = g_repeater_device->ctx[idx];
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx %p\n", __func__, ctx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       if (ctx->ctx_status == REPEATER_CTX_INIT) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       shr_bufs = &ctx->shared_bufs;
+
+       ret = set_captured_buf_idx(shr_bufs, buf_idx, capture_idx);
+
+       if (ctx->info.fps == 60) {
+               cur_ktime = ktime_get();
+               cur_timestamp = ktime_to_us(cur_ktime);
+               if (ret == 0) {
+                       ctx->enc_param.time_stamp = cur_timestamp;
+                       set_encoding_start(shr_bufs, buf_idx);
+                       ret = s5p_mfc_hwfc_encode(buf_idx, capture_idx, &ctx->enc_param);
+                       if (ret != HWFC_ERR_NONE) {
+                               print_repeater_debug(RPT_ERROR,
+                                       "s5p_mfc_hwfc_encode failed %d\n", ret);
+                               ret = set_encoding_done(shr_bufs);
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_EXT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int hwfc_encoding_done(int encoding_ret)
+{
+       int ret = 0;
+       struct repeater_context *ctx;
+       struct shared_buffer *shr_bufs;
+       int idx = REPEATER_CUR_CONTEXTS_NUM;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_EXT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (!g_repeater_device) {
+               print_repeater_debug(RPT_ERROR, "%s, g_repeater_device %p\n",
+                       __func__, g_repeater_device);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       ctx = g_repeater_device->ctx[idx];
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return -EFAULT;
+       }
+
+       shr_bufs = &ctx->shared_bufs;
+       ret = set_encoding_done(shr_bufs);
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_EXT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+void encoding_work_handler(struct work_struct *work)
+{
+       struct repeater_context *ctx;
+       struct shared_buffer *shr_bufs;
+       int32_t buf_idx = -1;
+       int32_t ret;
+       ktime_t cur_ktime;
+       uint64_t target_timestamp;
+       uint64_t cur_timestamp;
+       int32_t required_delay;
+       unsigned long flags;
+       struct delayed_work *enc_work;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       enc_work = container_of(work, struct delayed_work, work);
+       if (!enc_work) {
+               print_repeater_debug(RPT_ERROR, "no enc_work\n");
+               return;
+       }
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       ctx = container_of(enc_work, struct repeater_context, encoding_work);
+
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx %p\n", __func__, ctx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return;
+       }
+
+       if (ctx->ctx_status != REPEATER_CTX_START) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return;
+       }
+
+       cur_ktime = ktime_get();
+       cur_timestamp = ktime_to_us(cur_ktime);
+
+       target_timestamp = ctx->encoding_start_timestamp +
+               ctx->video_frame_count * ctx->encoding_period_us + ctx->paused_time;
+       required_delay = target_timestamp - cur_timestamp;
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_EXT_INFO, "usleep_range %d\n", required_delay);
+       if (required_delay > 1000 && required_delay < (1000000 / ctx->info.fps))
+               usleep_range(required_delay, required_delay + 1);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx %p\n", __func__, ctx);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return;
+       }
+
+       if (ctx->ctx_status != REPEATER_CTX_START) {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status %d\n",
+                       __func__, ctx->ctx_status);
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+               return;
+       }
+
+       shr_bufs = &ctx->shared_bufs;
+       ret = get_latest_captured_buf_idx(shr_bufs, &buf_idx);
+
+       cur_ktime = ktime_get();
+       cur_timestamp = ktime_to_us(cur_ktime);
+       if (ctx->encoding_start_timestamp == 0)
+               ctx->encoding_start_timestamp = cur_timestamp;
+       ctx->enc_param.time_stamp = ctx->encoding_start_timestamp +
+               ctx->video_frame_count * ctx->encoding_period_us + ctx->paused_time;
+       print_repeater_debug(RPT_EXT_INFO, "time_stamp %lld\n",
+       ctx->enc_param.time_stamp);
+
+       if (ret == 0 && buf_idx >= 0) {
+               print_repeater_debug(RPT_EXT_INFO, "buf_idx %d\n", buf_idx);
+               ctx->buf_idx_dump = buf_idx;
+               wake_up_interruptible(&ctx->wait_queue_dump);
+               set_encoding_start(shr_bufs, buf_idx);
+               ret = s5p_mfc_hwfc_encode(buf_idx, buf_idx, &ctx->enc_param);
+               if (ret != HWFC_ERR_NONE) {
+                       print_repeater_debug(RPT_ERROR,
+                               "s5p_mfc_hwfc_encode failed %d\n", ret);
+                       ret = set_encoding_done(shr_bufs);
+               }
+       }
+
+       do {
+               ctx->video_frame_count++;
+               target_timestamp = ctx->encoding_start_timestamp +
+                       ctx->video_frame_count * ctx->encoding_period_us + ctx->paused_time;
+               required_delay = target_timestamp - cur_timestamp;
+       } while (required_delay < 0);
+
+       print_repeater_debug(RPT_EXT_INFO, "timestamp cur %lld, target %lld\n",
+               cur_timestamp, target_timestamp);
+
+       if (ctx->ctx_status == REPEATER_CTX_START) {
+               required_delay = required_delay / 2;
+               print_repeater_debug(RPT_EXT_INFO, "schedule_delayed_work() %d\n",
+                       required_delay);
+               schedule_delayed_work(&ctx->encoding_work,
+                       usecs_to_jiffies(required_delay));
+       } else {
+               print_repeater_debug(RPT_ERROR, "ctx_status is invalid %d",
+                       ctx->ctx_status);
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+}
+
+int repeater_ioctl_map_buf(struct repeater_context *ctx)
+{
+       int ret = 0;
+       unsigned long flags;
+       int i = 0;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->info.buffer_count > MAX_SHARED_BUF_NUM) {
+               print_repeater_debug(RPT_ERROR, "%s, buffer_count is invalid %d",
+                       __func__, ctx->info.buffer_count);
+               ctx->info.buffer_count = MAX_SHARED_BUF_NUM;
+       }
+
+       if (ctx->info.fps != 30 && ctx->info.fps != 60) {
+               print_repeater_debug(RPT_ERROR, "%s, fps is invalid %d",
+                       __func__, ctx->info.fps);
+               ctx->info.fps = 30;
+       }
+
+       if (ctx->ctx_status == REPEATER_CTX_INIT) {
+               for (i = 0; i < ctx->info.buffer_count; i++) {
+                       ctx->dmabufs[i] = dma_buf_get(ctx->info.buf_fd[i]);
+                       print_repeater_debug(RPT_INT_INFO,
+                               "dmabufs[%i] %p\n", i, ctx->dmabufs[i]);
+               }
+               ctx->ctx_status = REPEATER_CTX_MAP;
+               ctx->encoding_period_us = ENCODING_PERIOD_TIME_US / ctx->info.fps;
+       } else {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d",
+                       __func__, ctx->ctx_status);
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_unmap_buf(struct repeater_context *ctx)
+{
+       int ret = 0;
+       unsigned long flags;
+       int i;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->info.buffer_count > MAX_SHARED_BUF_NUM) {
+               print_repeater_debug(RPT_ERROR, "%s, buffer_count is invalid %d",
+                       __func__, ctx->info.buffer_count);
+               ctx->info.buffer_count = MAX_SHARED_BUF_NUM;
+       }
+
+       if (ctx->ctx_status == REPEATER_CTX_MAP) {
+               for (i = 0; i < ctx->info.buffer_count; i++) {
+                       if (!IS_ERR_OR_NULL(ctx->dmabufs[i])) {
+                               dma_buf_put(ctx->dmabufs[i]);
+                               ctx->dmabufs[i] = 0;
+                       }
+               }
+               ctx->ctx_status = REPEATER_CTX_INIT;
+       } else {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d",
+                       __func__, ctx->ctx_status);
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_start(struct repeater_context *ctx)
+{
+       int ret = 0;
+       unsigned long flags;
+       struct shared_buffer *shr_bufs;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+       print_repeater_debug(RPT_INT_INFO, "ctx_status %d, fps %d\n",
+               ctx->ctx_status, ctx->info.fps);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->ctx_status == REPEATER_CTX_MAP) {
+               INIT_DELAYED_WORK(&ctx->encoding_work, encoding_work_handler);
+               shr_bufs = &ctx->shared_bufs;
+               init_shared_buffer(shr_bufs, MAX_SHARED_BUF_NUM);
+               ctx->ctx_status = REPEATER_CTX_START;
+               if (ctx->info.fps == 30) {
+                       ret = schedule_delayed_work(&ctx->encoding_work,
+                               usecs_to_jiffies(1));
+                       print_repeater_debug(RPT_INT_INFO,
+                               "schedule_delayed_work ret %d\n", ret);
+               }
+       } else {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d\n",
+                       __func__, ctx->ctx_status);
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_stop(struct repeater_context *ctx)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+       print_repeater_debug(RPT_INT_INFO, "ctx_status %d\n", ctx->ctx_status);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->ctx_status == REPEATER_CTX_START ||
+                       ctx->ctx_status == REPEATER_CTX_PAUSE) {
+               ctx->ctx_status = REPEATER_CTX_MAP;
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+               ret = cancel_delayed_work_sync(&ctx->encoding_work);
+               print_repeater_debug(RPT_INT_INFO,
+                       "cancel_delayed_work_sync ret %d\n", ret);
+       } else {
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d\n",
+                       __func__, ctx->ctx_status);
+       }
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_pause(struct repeater_context *ctx)
+{
+       int ret = 0;
+       ktime_t cur_ktime;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+       print_repeater_debug(RPT_INT_INFO, "ctx_status %d\n", ctx->ctx_status);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->ctx_status == REPEATER_CTX_START) {
+               cur_ktime = ktime_get();
+               ctx->pause_time = ktime_to_us(cur_ktime);
+               ctx->ctx_status = REPEATER_CTX_PAUSE;
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+               ret = cancel_delayed_work_sync(&ctx->encoding_work);
+               print_repeater_debug(RPT_INT_INFO,
+                       "cancel_delayed_work_sync ret %d\n", ret);
+       } else {
+               spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d\n",
+                       __func__, ctx->ctx_status);
+       }
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_resume(struct repeater_context *ctx)
+{
+       int ret = 0;
+       ktime_t cur_ktime;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+       print_repeater_debug(RPT_INT_INFO, "ctx_status %d\n", ctx->ctx_status);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->ctx_status == REPEATER_CTX_PAUSE) {
+               INIT_DELAYED_WORK(&ctx->encoding_work, encoding_work_handler);
+               cur_ktime = ktime_get();
+               ctx->resume_time = ktime_to_us(cur_ktime);
+               ctx->paused_time += ctx->resume_time - ctx->pause_time;
+               ctx->ctx_status = REPEATER_CTX_START;
+               ret = schedule_delayed_work(&ctx->encoding_work,
+                       usecs_to_jiffies(1));
+               print_repeater_debug(RPT_INT_INFO,
+                       "schedule_delayed_work ret %d\n", ret);
+       } else {
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d\n",
+                       __func__, ctx->ctx_status);
+       }
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+int repeater_ioctl_dump(struct repeater_context *ctx)
+{
+       int ret = 0;
+       unsigned long jiffies_from_usec = usecs_to_jiffies(MAX_WAIT_TIME_DUMP);
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       if (ctx->ctx_status == REPEATER_CTX_START)
+               ret = wait_event_interruptible_timeout(ctx->wait_queue_dump,
+                       ctx->buf_idx_dump >= 0, jiffies_from_usec);
+       else
+               print_repeater_debug(RPT_ERROR, "%s, ctx_status is invalid %d\n",
+                       __func__, ctx->ctx_status);
+
+       print_repeater_debug(RPT_INT_INFO,
+               "wait_event_interruptible_timeout(%lu) ret %d, ctx->buf_idx_dump %d\n",
+               jiffies_from_usec, ret, ctx->buf_idx_dump);
+
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+static int repeater_open(struct inode *inode, struct file *filp)
+{
+       int ret = 0;
+       struct repeater_device *repeater_dev = container_of(filp->private_data,
+               struct repeater_device, misc_dev);
+       struct repeater_context *ctx;
+       struct shared_buffer *shr_bufs;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       if (repeater_dev->ctx_num >= REPEATER_MAX_CONTEXTS_NUM) {
+               print_repeater_debug(RPT_ERROR, "%s, too many context\n", __func__);
+               ret = -EBUSY;
+               return ret;
+       }
+
+       ctx = kzalloc(sizeof(struct repeater_context), GFP_KERNEL);
+       if (!ctx) {
+               ret = -ENOMEM;
+               return ret;
+       }
+
+       ctx->repeater_dev = repeater_dev;
+       repeater_dev->ctx[repeater_dev->ctx_num] = ctx;
+       repeater_dev->ctx_num++;
+
+       filp->private_data = ctx;
+
+       ctx->ctx_status = REPEATER_CTX_INIT;
+
+       shr_bufs = &ctx->shared_bufs;
+       init_shared_buffer(shr_bufs, MAX_SHARED_BUF_NUM);
+       ctx->enc_param.time_stamp = 0;
+       ctx->encoding_start_timestamp = 0;
+       ctx->video_frame_count = 0;
+       ctx->paused_time = 0;
+       ctx->pause_time = 0;
+       ctx->resume_time = 0;
+       ctx->time_stamp_us = 33333;
+       ctx->last_encoding_time_us = 0;
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+       g_repeater_device = repeater_dev;
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       init_waitqueue_head(&ctx->wait_queue_dump);
+       ctx->buf_idx_dump = -1;
+
+       pm_runtime_get_sync(repeater_dev->dev);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+}
+
+static int repeater_release(struct inode *inode, struct file *filp)
+{
+       int ret = 0;
+       struct repeater_context *ctx = filp->private_data;
+       struct repeater_device *repeater_dev = ctx->repeater_dev;
+       unsigned long flags;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       if (ctx->ctx_status == REPEATER_CTX_START ||
+                       ctx->ctx_status == REPEATER_CTX_PAUSE)
+               repeater_ioctl_stop(ctx);
+
+       if (ctx->ctx_status == REPEATER_CTX_MAP)
+               repeater_ioctl_unmap_buf(ctx);
+
+       pm_runtime_put_sync(repeater_dev->dev);
+
+       spin_lock_irqsave(&repeater_spinlock, flags);
+
+       ctx->repeater_dev->ctx_num--;
+       kfree(ctx);
+       filp->private_data = NULL;
+
+       g_repeater_device = NULL;
+       spin_unlock_irqrestore(&repeater_spinlock, flags);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+       return ret;
+}
+
+static long repeater_ioctl(struct file *filp,
+       unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       struct repeater_context *ctx;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       ctx = filp->private_data;
+       if (!ctx) {
+               print_repeater_debug(RPT_ERROR, "ctx is NULL\n");
+               ret = -ENOTTY;
+               return ret;
+       }
+
+       switch (cmd) {
+       case REPEATER_IOCTL_MAP_BUF:
+               if (copy_from_user(&ctx->info,
+                       (struct repeater_info __user *)arg,
+                       sizeof(struct repeater_info))) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               ret = repeater_ioctl_map_buf(ctx);
+
+               if (copy_to_user((struct repeater_info __user *)arg,
+                       &ctx->info,
+                       sizeof(struct repeater_info))) {
+                       ret = -EFAULT;
+                       break;
+               }
+       break;
+
+       case REPEATER_IOCTL_UNMAP_BUF:
+               ret = repeater_ioctl_unmap_buf(ctx);
+       break;
+
+       case REPEATER_IOCTL_START:
+               ret = repeater_ioctl_start(ctx);
+       break;
+
+       case REPEATER_IOCTL_STOP:
+               ret = repeater_ioctl_stop(ctx);
+       break;
+
+       case REPEATER_IOCTL_PAUSE:
+               ret = repeater_ioctl_pause(ctx);
+       break;
+
+       case REPEATER_IOCTL_RESUME:
+               ret = repeater_ioctl_resume(ctx);
+       break;
+
+       case REPEATER_IOCTL_DUMP:
+               ret = repeater_ioctl_dump(ctx);
+               if (copy_to_user((int __user *)arg,
+                       &ctx->buf_idx_dump,
+                       sizeof(int))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               ctx->buf_idx_dump = -1;
+       break;
+
+       default:
+               ret = -ENOTTY;
+       }
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+       return ret;
+
+}
+
+static const struct file_operations repeater_fops = {
+       .owner          = THIS_MODULE,
+       .open           = repeater_open,
+       .release        = repeater_release,
+       .unlocked_ioctl = repeater_ioctl,
+       .compat_ioctl = repeater_ioctl,
+};
+
+static int repeater_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct repeater_device *repeater_dev;
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       repeater_dev = devm_kzalloc(&pdev->dev, sizeof(struct repeater_device),
+                                       GFP_KERNEL);
+       if (!repeater_dev)
+               return -ENOMEM;
+
+       repeater_dev->ctx_num = 0;
+       repeater_dev->dev = &pdev->dev;
+       repeater_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
+       repeater_dev->misc_dev.fops = &repeater_fops;
+       repeater_dev->misc_dev.name = NODE_NAME;
+       ret = misc_register(&repeater_dev->misc_dev);
+       if (ret)
+               goto err_misc_register;
+
+       platform_set_drvdata(pdev, repeater_dev);
+
+       pm_runtime_enable(&pdev->dev);
+
+       spin_lock_init(&repeater_spinlock);
+       g_repeater_debug_level = RPT_ERROR;
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return ret;
+
+err_misc_register:
+       print_repeater_debug(RPT_ERROR, "%s--, err_misc_dev\n", __func__);
+
+       return ret;
+}
+
+static int repeater_remove(struct platform_device *pdev)
+{
+       struct repeater_device *repeater_dev = platform_get_drvdata(pdev);
+
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       pm_runtime_disable(&pdev->dev);
+
+       if (repeater_dev) {
+               misc_deregister(&repeater_dev->misc_dev);
+               kfree(repeater_dev);
+       }
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+
+       return 0;
+}
+
+static void repeater_shutdown(struct platform_device *pdev)
+{
+       print_repeater_debug(RPT_INT_INFO, "%s++\n", __func__);
+
+       print_repeater_debug(RPT_INT_INFO, "%s--\n", __func__);
+}
+
+static const struct of_device_id exynos_repeater_match[] = {
+       {
+               .compatible = "samsung,exynos-repeater",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_repeater_match);
+
+static struct platform_driver repeater_driver = {
+       .probe          = repeater_probe,
+       .remove         = repeater_remove,
+       .shutdown       = repeater_shutdown,
+       .driver = {
+               .name   = MODULE_NAME,
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(exynos_repeater_match),
+       }
+};
+
+module_platform_driver(repeater_driver);
+
+MODULE_AUTHOR("Shinwon Lee <shinwon.lee@samsung.com>");
+MODULE_DESCRIPTION("EXYNOS repeater driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos/repeater/repeater_dev.h b/drivers/media/platform/exynos/repeater/repeater_dev.h
new file mode 100644 (file)
index 0000000..ffe9679
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Header file for Exynos REPEATER driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REPEATER_DEV_H_
+#define _REPEATER_DEV_H_
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/types.h>
+#include <linux/dma-buf.h>
+#include <linux/timer.h>
+
+#include <media/exynos_repeater.h>
+#include <media/s5p_mfc_hwfc.h>
+
+#include "repeater.h"
+#include "repeater_buf.h"
+
+#define REPEATER_MAX_CONTEXTS_NUM              1
+
+enum repeater_context_status {
+       REPEATER_CTX_INIT,
+       REPEATER_CTX_MAP,
+       REPEATER_CTX_START,
+       REPEATER_CTX_PAUSE,
+};
+
+struct repeater_device {
+       struct miscdevice misc_dev;
+       struct device *dev;
+       int ctx_num;
+       struct repeater_context *ctx[REPEATER_MAX_CONTEXTS_NUM];
+};
+
+struct repeater_context {
+       struct repeater_device *repeater_dev;
+       struct repeater_info info;
+       struct dma_buf *dmabufs[MAX_SHARED_BUF_NUM];
+
+       struct shared_buffer shared_bufs;
+       struct encoding_param enc_param;
+       struct timer_list encoding_timer;
+       uint64_t encoding_period_us;
+       uint64_t last_encoding_time_us;
+       uint64_t time_stamp_us;
+       enum repeater_context_status ctx_status;
+
+       struct delayed_work encoding_work;
+       uint64_t encoding_start_timestamp;
+       uint64_t video_frame_count;
+       uint64_t paused_time;
+       uint64_t pause_time;
+       uint64_t resume_time;
+
+       /* To dump data */
+       wait_queue_head_t wait_queue_dump;
+       int buf_idx_dump;
+};
+
+#define NODE_NAME              "repeater"
+#define MODULE_NAME            "exynos-repeater"
+
+#endif /* _REPEATER_DEV_H_ */