From 01a06f3f022a0d0e04ebd61edd490203c02c4914 Mon Sep 17 00:00:00 2001 From: "shinwon.lee" Date: Fri, 15 Jun 2018 14:17:49 +0900 Subject: [PATCH] media: repeater: init version Change-Id: I64a3967087873f5fbc48b2d145d1648b31f4d03c Signed-off-by: shinwon.lee --- .../media/platform/exynos/repeater/Kconfig | 8 + .../media/platform/exynos/repeater/Makefile | 9 + .../media/platform/exynos/repeater/repeater.h | 45 + .../platform/exynos/repeater/repeater_buf.c | 221 +++++ .../platform/exynos/repeater/repeater_buf.h | 51 ++ .../platform/exynos/repeater/repeater_dbg.h | 29 + .../platform/exynos/repeater/repeater_dev.c | 851 ++++++++++++++++++ .../platform/exynos/repeater/repeater_dev.h | 75 ++ 8 files changed, 1289 insertions(+) create mode 100644 drivers/media/platform/exynos/repeater/Kconfig create mode 100644 drivers/media/platform/exynos/repeater/Makefile create mode 100644 drivers/media/platform/exynos/repeater/repeater.h create mode 100644 drivers/media/platform/exynos/repeater/repeater_buf.c create mode 100644 drivers/media/platform/exynos/repeater/repeater_buf.h create mode 100644 drivers/media/platform/exynos/repeater/repeater_dbg.h create mode 100644 drivers/media/platform/exynos/repeater/repeater_dev.c create mode 100644 drivers/media/platform/exynos/repeater/repeater_dev.h diff --git a/drivers/media/platform/exynos/repeater/Kconfig b/drivers/media/platform/exynos/repeater/Kconfig new file mode 100644 index 000000000000..da86f093d0ae --- /dev/null +++ b/drivers/media/platform/exynos/repeater/Kconfig @@ -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 index 000000000000..13050c4509fb --- /dev/null +++ b/drivers/media/platform/exynos/repeater/Makefile @@ -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 index 000000000000..5e45ba89461f --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater.h @@ -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 index 000000000000..0e530d8346de --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater_buf.c @@ -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 + +#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 index 000000000000..5271417b385f --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater_buf.h @@ -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 + +#include + +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 index 000000000000..c5923df42c64 --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater_dbg.h @@ -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 index 000000000000..cd53b374712d --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater_dev.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..ffe96794d807 --- /dev/null +++ b/drivers/media/platform/exynos/repeater/repeater_dev.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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_ */ -- 2.20.1