/**
 * @file sony_fpga_ioctl.c
 * @brief sony_fpgaのioctl実装
 */
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <nv-p2p.h>
#include <asm/cacheflush.h>
#include "./sony_fpga_cmd.h"
#include "./sony_fpga.h"
#include "./sony_fpga_xpt.h"
#include "./sony_fpga_sis.h"

#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/path.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/crc16.h>

//#define REPORT_ENABLE

extern u32*              process_status_area;
extern u32*              process_status_area_xpt;
extern int               module_param_ctrl_dmac_debug_enable;
extern u32               vd_counter;
extern vd_wait_queue_t   vd_wait_queue;
extern u32               int_counter;

static int module_param_debug_print = 0;
module_param(module_param_debug_print, int, S_IRUGO | S_IWUSR);

static int module_param_afm_wait = 100;
module_param(module_param_afm_wait, int, S_IRUGO | S_IWUSR);

static int video_dmac_stop_wait = 80;
module_param(video_dmac_stop_wait, int, S_IRUGO | S_IWUSR);
static int hbm_reset_wait_cnt = 4;
module_param(hbm_reset_wait_cnt, int, S_IRUGO | S_IWUSR);

static int module_param_aurora_reset_wait = 1000;
module_param(module_param_aurora_reset_wait, int, S_IRUGO | S_IWUSR);

static int module_param_aurora_test_wait = 10;
module_param(module_param_aurora_test_wait, int, S_IRUGO | S_IWUSR);

extern ulong module_param_gif1_busno;
#define IGNORE_LIST_MAX 255
struct ctrl_dmac_ignore_mng ignore_list = {0};

/* ioctl functions */
static int  sony_fpga_open(struct inode *inode, struct file *file);
static int  sony_fpga_release(struct inode *inode, struct file *file);
static long sony_fpga_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
static int sony_fpga_mmap(struct file* file, struct vm_area_struct* vma);
static long sony_fpga_reg_write(struct sony_fpga_ctrl* ctrl_data,
                               const struct fpga_reg_write_arg __user* ptr, u32 len);
static long sony_fpga_reg_write_debug(struct sony_fpga_ctrl* ctrl_data,
                               const struct fpga_reg_write_arg __user* ptr, u32 len);
static long sony_fpga_reg_update_ok(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_reg_update_completed(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_pause_video_dmac(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_restart_video_dmac(struct sony_fpga_ctrl* ctrl_data);
#ifndef GPU_DISABLE
static long sony_fpga_make_video_descriptor(struct sony_fpga_ctrl* ctrl_data,
                               struct video_dmac_region __user* ptr, u32 len);
static long sony_fpga_reset_video_descriptor(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_set_video_descriptor(struct sony_fpga_ctrl* ctrl_data,
                               struct video_dmac_descriptor __user* ptr, u32 len);
static long sony_fpga_pin_gpu_memory(struct sony_fpga_ctrl* ctrl_data,
                                         struct gpu_mem_info __user* ptr, u32 len);
static long sony_fpga_set_video_dmac_control(struct sony_fpga_ctrl* ctrl_data, u32 __user* arg, u32 len);
static long sony_fpga_set_video_dmac_channel(struct sony_fpga_ctrl* ctrl_data, struct video_dmac_channel_setting __user* ptr, u32 len);
#endif
static long sony_fpga_set_multi_cast_address(struct sony_fpga_ctrl* ctrl_data, struct multicast_info __user* ptr, u32 len);
static long sony_fpga_wait_vd(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_wait_vd_info(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_wait_vd_info_timeout(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_video_file(struct sony_fpga_ctrl* ctrl_data, struct video_file_info __user* ptr, u32 len, uint rw);

static long sony_fpga_refsync_mon_clear(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_set_video_format(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_video_format_interrupt(struct sony_fpga_ctrl* ctrl_data);
/* other functions */
#ifndef GPU_DISABLE
static long assign_video_desc_set(struct sony_fpga_ctrl* ctrl_data, struct video_dmac_region* region);
static int get_gpu_page_size(const nvidia_p2p_page_table_t* table);
static long write_video_descriptor(struct dma_desc* desc_head, struct video_dmac_region* param, dma_addr_t target_base_addr, bool last);
#endif
static long sony_fpga_ctrl_dmac_ignore_address(struct sony_fpga_ctrl* ctrl_data, struct ctrl_dmac_ignore_list __user* ptr, u32 len);
static long sony_fpga_ctrl_dmac_ignore_list_clear(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_cmp_test_ctrl(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_set_sdi_out_ch(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_ref_lock(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_audio_mute(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_audio_file(struct sony_fpga_ctrl* ctrl_data, struct audio_file_info __user* arg, u32 len, uint rw);
static long sony_fpga_slavedata_file_write(struct sony_fpga_ctrl* ctrl_data, struct slavedata_write_info __user* arg, u32 len);
static long sony_fpga_slavedata_update_start(struct sony_fpga_ctrl* ctrl_data, struct slavedata_start_param __user* arg, u32 len);
static long sony_fpga_slavedata_update_finish(struct sony_fpga_ctrl* ctrl_data, u32 __user* arg, u32 len);
static long sony_fpga_slavedata_update_progress(struct sony_fpga_ctrl* ctrl_data, u32 __user* arg, u32 len);
static long sony_fpga_slavedata_update_error_clear(struct sony_fpga_ctrl* ctrl_data, u32 __user* arg, u32 len);
static long sony_fpga_set_video_transfer(struct sony_fpga_ctrl* ctrl_data, u32 __user* arg, u32 len);
static long sony_fpga_rtp_aurora_reset(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_rtp_aurora_check(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_aurora_nstop_release(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_c2c_aurora_reset(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_swr_aurora_reset(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_xpt_aurora_reset(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_bim_aurora_reset(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_get_aurora_status(struct sony_fpga_ctrl* ctrl_data, struct aurora_status_param __user* ptr, u32 len);
static long sony_fpga_i2c_tx(struct sony_fpga_ctrl* ctrl_data, struct i2c_param __user* ptr, u32 len);
static long sony_fpga_i2c_rx(struct sony_fpga_ctrl* ctrl_data, struct i2c_param __user* ptr, u32 len);
static long sony_fpga_get_qsfp_status(struct sony_fpga_ctrl* ctrl_data, struct qsfp_status_param __user* ptr, u32 len);
static long sony_fpga_set_box_mode(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_qsfp_led(struct sony_fpga_ctrl* ctrl_data, struct qsfp_led_param __user* ptr, u32 len);
static long sony_fpga_qsfp_aurora_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_qsfp_initialize(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_qsfp_cdr_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_qsfp_thread(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len, bool pause);
static long sony_fpga_enter_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_start_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data, struct aurora_test_result __user* ptr, u32 len);
static long sony_fpga_exit_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_enable_gif_delay_adjustment(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_write_serial_number(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_set_axi_firewall_wait(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_aurora_init(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_set_sdi_mute(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_set_sdi_auto_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len);
static long sony_fpga_mib_start(struct sony_fpga_ctrl* ctrl_data);
static long sony_fpga_reset_mib_counter(struct sony_fpga_ctrl* ctrl_data);
/* other functions */
static long pin_user_page(struct sony_fpga_ctrl* ctrl, void** kmem, void* buffer, u32 buffer_size, uint* nr_pages, struct page*** pages);
static long free_user_page(void* kmem, uint nr_pages, struct page** pages);
static void pug_file_dma_start(struct sony_fpga_register __iomem* reg, u32 command);
// Primitive型の単純レジスタ設定用インタフェース
#define DECLARE_REGSET_FUNCTION(name, type) static long sony_fpga_set_##name(struct sony_fpga_ctrl* ctrl_data, type __user* ptr, u32 len)

void video_dmac_start_func(struct work_struct* work);

DECLARE_REGSET_FUNCTION(irq_vd_control, u32);
DECLARE_REGSET_FUNCTION(ctrl_master_mode, u32);
DECLARE_REGSET_FUNCTION(ctrl_dmac_control, u32);
DECLARE_REGSET_FUNCTION(multi_cast_start, u32);

static int sony_fpga_mmap(struct file *file, struct vm_area_struct *vma)
{
    struct sony_fpga_ctrl* ctrl_data;

    ctrl_data = (struct sony_fpga_ctrl*)file->private_data;
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (ctrl_data->ctrl_dmac_buffer == NULL) {
        return -ENODEV;
    }
    return dma_mmap_coherent(CDEVICE(ctrl_data), vma,
                             ctrl_data->ctrl_dmac_buffer,
                             ctrl_data->ctrl_dmac_buffer_bus_addr,
                             ctrl_data->ctrl_dmac_pages * PAGE_SIZE);

}
struct file_operations sony_fpga_fops =
{
    .owner          = THIS_MODULE,
    .open           = sony_fpga_open,
    .release        = sony_fpga_release,
    .unlocked_ioctl = sony_fpga_ioctl,
    .mmap           = sony_fpga_mmap
};

#ifndef GPU_DISABLE
static void free_callback(void *data)
{
    struct sony_fpga_ctrl* ctrl_data = data;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return;
    }
    dev_info(CDEVICE(ctrl_data), "free_callback()\n");
    nvidia_p2p_free_page_table(ctrl_data->gd.pages);
    ctrl_data->gd.pages = NULL;
}

#define GPU_BOUND_SHIFT  (16)
#define GPU_BOUND_SIZE   ((u64)1 << GPU_BOUND_SHIFT)
#define GPU_BOUND_OFFSET (GPU_BOUND_SIZE-1)
#define GPU_BOUND_MASK   (~GPU_BOUND_OFFSET)
static long kmd_pin_memory(struct sony_fpga_ctrl* ctrl_data, void *address, size_t size)
{
    // do proper alignment, as required by NVIDIA kernel driver
    u64 virt_start = (u64)address & GPU_BOUND_MASK;
    size_t pin_size = (u64)address + size - virt_start;
    int ret;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EINVAL;
    }
    if (!size) {
        dev_err(CDEVICE(ctrl_data), "kmd_pin_memory() : illegal size \n");
        return -EINVAL;
    }

    ret = nvidia_p2p_get_pages(0, 0, virt_start, pin_size, &ctrl_data->gd.pages, free_callback, ctrl_data);
    if (ret == 0) {
        dev_info(CDEVICE(ctrl_data), "kmd_pin_memory() : pinned successfully. virt_start = 0x%llx pin_size = %lld %d\n",
               virt_start, (long long)pin_size, smp_processor_id());
        dev_info(CDEVICE(ctrl_data), "page_size = 0x%x entries = %d physical address[0] = 0x%llx %d\n",
               ctrl_data->gd.pages->page_size,
               ctrl_data->gd.pages->entries,
               ctrl_data->gd.pages->pages[0]->physical_address, smp_processor_id());
        // Succesfully pinned, page_table can be accessed
    } else {
        dev_err(CDEVICE(ctrl_data), "kmd_pin_memory() : pinning failed. virt_start = 0x%llx pin_size = %lld\n",
               virt_start, (long long)pin_size);
        // Pinning failed
        return -EFAULT;
    }

    if (!NVIDIA_P2P_PAGE_TABLE_VERSION_COMPATIBLE(ctrl_data->gd.pages)) {
        dev_err(CDEVICE(ctrl_data), "kmd_pin_memory() : nvidia_p2p_page_table version mismatch. vesion = 0x%x\n",
               ctrl_data->gd.pages->version);
        nvidia_p2p_free_page_table(ctrl_data->gd.pages);
        return -EFAULT;
    }

    return ret;
}
#endif

static long pin_user_page(struct sony_fpga_ctrl* ctrl, void** kmem, void* buffer, u32 buffer_size, uint* nr_pages, struct page*** pages)
{
    long pinned;
    uint i;

    *nr_pages = buffer_size / 4096 + 1;
    *pages = (struct page**)kvcalloc(*nr_pages, sizeof(struct page*), GFP_KERNEL);
    if (!*pages) {
        dev_err(CDEVICE(ctrl), "PAGE_ALLOC_ERROR\n");
        return -EFAULT;
    }
    pinned = get_user_pages_fast(
            ((unsigned long)buffer & PAGE_MASK),
            *nr_pages, 1, *pages);
    if (pinned != *nr_pages) {
        for (i = 0; i < pinned; i++) {
            put_page(*pages[i]);
        }
        kvfree(*pages);
        dev_err(CDEVICE(ctrl), "pin failed\n");
        return -EFAULT;
    }
    *kmem = vmap(*pages, *nr_pages, VM_MAP, PAGE_KERNEL);
    if (*kmem == NULL) {
        for (i = 0; i < *nr_pages; i++) {
            set_page_dirty(*pages[i]);
            put_page(*pages[i]);
        }
        kvfree(*pages);
        return -ENOMEM;
    }
    return 0;
}
static long free_user_page(void* kmem, uint nr_pages, struct page** pages)
{
    uint i;

    vunmap(kmem);
    for (i = 0; i < nr_pages; i++) {
        set_page_dirty(pages[i]);
        put_page(pages[i]);
    }
    kvfree(pages);

    return 0;
}

static void pug_file_dma_start(struct sony_fpga_register __iomem* reg, u32 command)
{
    void __iomem* addr;
    addr = &(reg->dmac_file.status_clear);
    IO_WRITE_32(1, addr);

    addr = &(reg->file_dmac_control);
    IO_WRITE_32(command, addr);
    wmb();
}

int sony_fpga_open(struct inode* inode, struct file* file)
{
    struct sony_fpga_ctrl* ctrl_data = NULL;

    ctrl_data = container_of(inode->i_cdev, struct sony_fpga_ctrl, cdev);
    if (ctrl_data == NULL || file == NULL) {
        printk(KERN_ERR "%s ctrl_data or file is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    file->private_data = ctrl_data;
    ctrl_data->user_pid = current->pid; /* 本来複数で開かれたくはないが最後に開いた人が記録される */

    return 0;
}

int sony_fpga_release(struct inode* inode, struct file* file)
{
    return 0;
}

static long sony_fpga_ioctl (struct file* filp, unsigned int cmd, unsigned long arg)
{
    struct sony_fpga_ctrl *ctrl_data = filp->private_data;
    if (_IOC_TYPE(cmd) != SONY_FPGA_IOC_MAGIC) {
        return -ENOTTY;
    }
    switch (cmd) {
    case SONY_FPGA_REG_WRITE:
        if (module_param_ctrl_dmac_debug_enable != 1) {
            return sony_fpga_reg_write(ctrl_data, (struct fpga_reg_write_arg __user*)arg, _IOC_SIZE(cmd));
        } else {
            return sony_fpga_reg_write_debug(ctrl_data, (struct fpga_reg_write_arg __user*)arg, _IOC_SIZE(cmd));
        }
    case SONY_FPGA_WAIT_VD_INFO:
        return sony_fpga_wait_vd_info(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_WAIT_VD_INFO_TIMEOUT:
        return sony_fpga_wait_vd_info_timeout(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_REG_UPDATE_OK:
        return sony_fpga_reg_update_ok(ctrl_data);
    case SONY_FPGA_REG_UPDATE_COMPLETED:
        return sony_fpga_reg_update_completed(ctrl_data);
    case SONY_FPGA_GET_AURORA_STATUS:
        return sony_fpga_get_aurora_status(ctrl_data, (struct aurora_status_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_GET_QSFP_STATUS:
        return sony_fpga_get_qsfp_status(ctrl_data, (struct qsfp_status_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_AUDIO_MUTE:
        return sony_fpga_audio_mute(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_VIDEO_FILE_WRITE:
        return sony_fpga_video_file(ctrl_data, (struct video_file_info __user*)arg, _IOC_SIZE(cmd), DIRECTION_WRITE);
    case SONY_FPGA_VIDEO_FILE_READ:
        return sony_fpga_video_file(ctrl_data, (struct video_file_info __user*)arg, _IOC_SIZE(cmd), DIRECTION_READ);
    case SONY_FPGA_AUDIO_FILE_WRITE:
        return sony_fpga_audio_file(ctrl_data, (struct audio_file_info __user*)arg, _IOC_SIZE(cmd), DIRECTION_WRITE);
    case SONY_FPGA_AUDIO_FILE_READ:
        return sony_fpga_audio_file(ctrl_data, (struct audio_file_info __user*)arg, _IOC_SIZE(cmd), DIRECTION_READ);
    case SONY_FPGA_SET_SDI_OUT_CH:
        return sony_fpga_set_sdi_out_ch(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_PAUSE_VIDEO_DMAC:
        return sony_fpga_pause_video_dmac(ctrl_data);
    case SONY_FPGA_RESTART_VIDEO_DMAC:
        return sony_fpga_restart_video_dmac(ctrl_data);
    case SONY_FPGA_REF_LOCK:
        return sony_fpga_ref_lock(ctrl_data);
    case SONY_FPGA_SLAVEDATA_FILE_WRITE:
        return sony_fpga_slavedata_file_write(ctrl_data, (struct slavedata_write_info __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SLAVEDATA_UPDATE_PROGRESS:
        return sony_fpga_slavedata_update_progress(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SLAVEDATA_UPDATE_ERROR_CLEAR:
        return sony_fpga_slavedata_update_error_clear(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_QSFP_LED:
        return sony_fpga_qsfp_led(ctrl_data, (struct qsfp_led_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_QSFP_AURORA_RESET:
        return sony_fpga_qsfp_aurora_reset(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_QSFP_INITIALIZE:
        return sony_fpga_qsfp_initialize(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_QSFP_CDR_RESET:
        return sony_fpga_qsfp_cdr_reset(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
#ifndef GPU_DISABLE
    case SONY_FPGA_MAKE_VIDEO_DESCRIPTOR:
        return sony_fpga_make_video_descriptor(ctrl_data, (struct video_dmac_region __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_VIDEO_DESCRIPTOR:
        return sony_fpga_set_video_descriptor(ctrl_data, (struct video_dmac_descriptor __user*)arg, _IOC_SIZE(cmd));
#endif
    case SONY_FPGA_SET_MULTICAST_ADDRESS:
        return sony_fpga_set_multi_cast_address(ctrl_data, (struct multicast_info __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_VD_INT_ENABLE:
        return sony_fpga_set_irq_vd_control(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_CTRL_DMAC:
        return sony_fpga_set_ctrl_dmac_control(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_MULTICAST:
        return sony_fpga_set_multi_cast_start(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
#ifndef GPU_DISABLE
    case SONY_FPGA_SET_VIDEO_DMAC:
        return sony_fpga_set_video_dmac_control(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_PIN_GPU_MEMORY:
        return sony_fpga_pin_gpu_memory(ctrl_data, (struct gpu_mem_info __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_RESET_VIDEO_DESCRIPTOR:
        return sony_fpga_reset_video_descriptor(ctrl_data);
    case SONY_FPGA_SET_VIDEO_DMAC_CHANNEL:
        return sony_fpga_set_video_dmac_channel(ctrl_data, (struct video_dmac_channel_setting __user*)arg, _IOC_SIZE(cmd));
#endif
    case SONY_FPGA_ENTER_AURORA_TESTMODE:
        return sony_fpga_enter_aurora_test_mode(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_START_AURORA_TESTMODE:
        return sony_fpga_start_aurora_test_mode(ctrl_data, (struct aurora_test_result __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_EXIT_AURORA_TESTMODE:
        return sony_fpga_exit_aurora_test_mode(ctrl_data);
    case SONY_FPGA_SET_VIDEO_TRANSFER:
        return sony_fpga_set_video_transfer(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_MIB_START:
        return sony_fpga_mib_start(ctrl_data);
    case SONY_FPGA_RESET_MIB_COUNTER:
        return sony_fpga_reset_mib_counter(ctrl_data);
    case SONY_FPGA_CTRL_DMAC_IGNORE_ADDRESS:
        return sony_fpga_ctrl_dmac_ignore_address(ctrl_data, (struct ctrl_dmac_ignore_list __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_CTRL_DMAC_IGNORE_LIST_CLEAR:
        return sony_fpga_ctrl_dmac_ignore_list_clear(ctrl_data);
    /* Debug用途 */
    case SONY_FPGA_SET_MASTER_MODE:
        return sony_fpga_set_ctrl_master_mode(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_READ_FRAME_CNT:
        return copy_to_user((u32 __user*)arg, &vd_counter, sizeof(u32));
    case SONY_FPGA_CMP_TEST_CTRL:
        return sony_fpga_cmp_test_ctrl(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_REFSYNC_MON_CLEAR:
        return sony_fpga_refsync_mon_clear(ctrl_data);
    case SONY_FPGA_SET_VIDEO_FORMAT:
        return sony_fpga_set_video_format(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_VIDEO_FORMAT_INTR:
        return sony_fpga_video_format_interrupt(ctrl_data);
    case SONY_FPGA_SLAVEDATA_UPDATE_START:
        return sony_fpga_slavedata_update_start(ctrl_data, (struct slavedata_start_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SLAVEDATA_UPDATE_FINISH:
        return sony_fpga_slavedata_update_finish(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_AXI_FIREWALL_WAIT:
        return sony_fpga_set_axi_firewall_wait(ctrl_data);
    case SONY_FPGA_AURORA_INIT:
        return sony_fpga_aurora_init(ctrl_data);
    case SONY_FPGA_SET_BOX_MODE:
        return sony_fpga_set_box_mode(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_RTP_AURORA_RESET:
        return sony_fpga_rtp_aurora_reset(ctrl_data);
    case SONY_FPGA_RTP_AURORA_CHECK:
        return sony_fpga_rtp_aurora_check(ctrl_data);
    case SONY_FPGA_AURORA_NSTOP_RELEASE:
        return sony_fpga_aurora_nstop_release(ctrl_data);
    case SONY_FPGA_C2C_AURORA_RESET:
        return sony_fpga_c2c_aurora_reset(ctrl_data);
    case SONY_FPGA_SWR_AURORA_RESET:
        return sony_fpga_swr_aurora_reset(ctrl_data);
    case SONY_FPGA_XPT_AURORA_RESET:
        return sony_fpga_xpt_aurora_reset(ctrl_data);
    case SONY_FPGA_BIM_AURORA_RESET:
        return sony_fpga_bim_aurora_reset(ctrl_data);
    case SONY_FPGA_ENABLE_GIF_DELAY_ADJUSTMENT:
        return sony_fpga_enable_gif_delay_adjustment(ctrl_data);
    case SONY_FPGA_WRITE_SERIAL_NUMBER:
        return sony_fpga_write_serial_number(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_XPT_I2C_TX:
        return sony_fpga_i2c_tx(ctrl_data, (struct i2c_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_XPT_I2C_RX:
        return sony_fpga_i2c_rx(ctrl_data, (struct i2c_param __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_QSFP_PAUSE_THREAD:
        return sony_fpga_qsfp_thread(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd), true);
    case SONY_FPGA_QSFP_RESUME_THREAD:
        return sony_fpga_qsfp_thread(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd), false);
    case SONY_FPGA_SET_SDI_MUTE:
        return sony_fpga_set_sdi_mute(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));
    case SONY_FPGA_SET_SDI_AUTO_RESET:
        return sony_fpga_set_sdi_auto_reset(ctrl_data, (u32 __user*)arg, _IOC_SIZE(cmd));

    /* TODO 以降は削除するかも */
    case SONY_FPGA_WAIT_VD:
        return sony_fpga_wait_vd(ctrl_data);
    default:
        break;
    }
    return -EINVAL;
}

/*********** ioctl functions ************/
static long sony_fpga_reg_write(struct sony_fpga_ctrl* ctrl_data,
                                const struct fpga_reg_write_arg __user* ptr, u32 len)
{
    struct fpga_reg_write_arg arg;
    u32  write_size;
    u32* rp;
    u32* buff;
    u32  dst_address;
    uint i;
    int  rc;
    u32  buffer_size = 256 * 1024; // Read DMA Sizeの上限値CTRL_DMAC_PAGESの最大値

    arg.dst_address = 0;
    arg.reg_top = NULL;
    arg.size = 0;
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct fpga_reg_write_arg) == len && copy_from_user(&arg, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (buffer_size >= arg.size) {
        buffer_size = arg.size;
    } else {
        dev_err(CDEVICE(ctrl_data), "%s : arg.size is too large\n", __FUNCTION__);
        return -EINVAL;
    }
    if (((ctrl_data->ctrl_dmac_pages - CTRL_DMAC_WRITE_DMA_PAGES) * PAGE_SIZE) < arg.size + (arg.size / 256 * 4)) {
        dev_err(CDEVICE(ctrl_data), "%s : arg.size is too large for ctrl_dmac_pages  \n", __FUNCTION__);
        return -EINVAL;
    }

    buff = (u32*)kmalloc(buffer_size, GFP_KERNEL);
    if (buff == NULL || copy_from_user(buff, (void __user*)arg.reg_top, buffer_size) != 0) {
        dev_err(CDEVICE(ctrl_data), "%s reg_top is NULL\n", __FUNCTION__);
        kfree(buff);
        return -EFAULT;
    }
    rp = buff;
    dst_address = arg.dst_address;
    write_size = 256;

    // PUG制御 I/F仕様に合わせて書き込む
    // APIは複数スレッドから随時呼ばれる想定
    rc = mutex_lock_interruptible(&ctrl_data->ctrl_dmac_mutex);
    if (rc != 0) {
        dev_err(CDEVICE(ctrl_data), "%s mutex lock error\n", __FUNCTION__);
        kfree(buff);
        return rc;
    }

    for (i = 0 ; i < arg.size / 256; i++) {
        *ctrl_data->ctrl_dmac_wp = dst_address;
        ctrl_data->ctrl_dmac_wp++;

        memcpy(ctrl_data->ctrl_dmac_wp, rp, write_size);
        ctrl_data->ctrl_dmac_wp += write_size / sizeof(u32);
        rp += write_size / sizeof(u32);
        dst_address += write_size;
    }
    if (arg.size % 256) {
        *ctrl_data->ctrl_dmac_wp = dst_address;
        ctrl_data->ctrl_dmac_wp++;

        write_size = arg.size % 256;
        memcpy(ctrl_data->ctrl_dmac_wp, rp, write_size);
        ctrl_data->ctrl_dmac_wp += write_size / sizeof(u32);

        memset(ctrl_data->ctrl_dmac_wp, 0xcd, 256 - write_size);
        ctrl_data->ctrl_dmac_wp += (256 - write_size) / sizeof(u32);
    }
    mutex_unlock(&ctrl_data->ctrl_dmac_mutex);
    kfree(buff);

    return 0;
}

static int sony_fpga_judge_address(u32 dst_address)
{
    uint i = 0;
    for (i = 0; i < ignore_list.block_cnt; i++) {
        if (ignore_list.data[i] == dst_address) {
#ifdef DEBUG
            printk(KERN_DEBUG "ignore:%x\n", dst_address);
#endif
            return 1;
        }
    }
    return 0;
}
static long sony_fpga_reg_write_debug(struct sony_fpga_ctrl* ctrl_data,
                                const struct fpga_reg_write_arg __user* ptr, u32 len)
{
    struct fpga_reg_write_arg arg;
    u32  write_size;
    u32* rp;
    u32* buff;
    u32  dst_address;
    uint i;
    int  rc;
    u32  buffer_size = 256 * 1024; // Read DMA Sizeの上限値CTRL_DMAC_PAGESの最大値

    arg.dst_address = 0;
    arg.reg_top = NULL;
    arg.size = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct fpga_reg_write_arg) == len && copy_from_user(&arg, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (buffer_size >= arg.size) {
        buffer_size = arg.size;
    } else {
        dev_err(CDEVICE(ctrl_data), "%s : arg.size is too large\n", __FUNCTION__);
        return -EINVAL;
    }

    buff = (u32*)kmalloc(buffer_size, GFP_KERNEL);
    if (buff == NULL || copy_from_user(buff, (void __user*)arg.reg_top, buffer_size) != 0) {
        dev_err(CDEVICE(ctrl_data), "%s reg_top is NULL\n", __FUNCTION__);
        kfree(buff);
        return -EFAULT;
    }
    rp = buff;
    dst_address = arg.dst_address;
    write_size = 256;

    // PUG制御 I/F仕様に合わせて書き込む
    // APIは複数スレッドから随時呼ばれる想定
    rc = mutex_lock_interruptible(&ctrl_data->ctrl_dmac_mutex);
    if (rc != 0) {
        kfree(buff);
        return rc;
    }

    for (i = 0 ; i < arg.size / 256; i++) {
        if (sony_fpga_judge_address(dst_address) == 1) {
            dst_address += write_size;
            rp += write_size / sizeof(u32);
            continue;
        }
        *ctrl_data->ctrl_dmac_wp = dst_address;
        ctrl_data->ctrl_dmac_wp++;

        memcpy(ctrl_data->ctrl_dmac_wp, rp, write_size);
        ctrl_data->ctrl_dmac_wp += write_size / sizeof(u32);
        rp += write_size / sizeof(u32);
        dst_address += write_size;
    }
    if (arg.size % 256) {
        *ctrl_data->ctrl_dmac_wp = dst_address;
        ctrl_data->ctrl_dmac_wp++;

        write_size = arg.size % 256;
        memcpy(ctrl_data->ctrl_dmac_wp, rp, write_size);
        ctrl_data->ctrl_dmac_wp += write_size / sizeof(u32);

        memset(ctrl_data->ctrl_dmac_wp, 0xcd, 256 - write_size);
        ctrl_data->ctrl_dmac_wp += (256 - write_size) / sizeof(u32);
    }
    mutex_unlock(&ctrl_data->ctrl_dmac_mutex);
    kfree(buff);

    return 0;
}

extern const u32 CTRL_DMAC_WRITE_SIZE[CTRL_DMAC_WRITE_MAX][FPGA_KIND_MAX];
static long sony_fpga_reg_update_ok(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    int  rc;
    u64  read_dma_size;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    rc = mutex_lock_interruptible(&ctrl_data->ctrl_dmac_mutex);
    if (rc != 0) {
        return rc;
    }
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->ctrl_dmac_size);

    read_dma_size = (u64)ctrl_data->ctrl_dmac_wp - (u64)(ctrl_data->ctrl_dmac_buffer + (CTRL_DMAC_WRITE_DMA_PAGES * PAGE_SIZE));
    if (read_dma_size % 128) {
        // BaseAddressとして無効値を設定しておく.
        // 128byte単位で転送するため前回の値が残っていても有効データとして判断されてしまうため.
        memset(ctrl_data->ctrl_dmac_wp, 0x00, 128 - read_dma_size % 128);
    }

    IO_WRITE_32(CTRL_DMAC_WRITE_SIZE[ctrl_data->cmp_test_ctrl][FPGA_KIND(ctrl_data->fpga_uniq_id)] |
            ((read_dma_size + 127) / 128) << 16,
            addr);

    ctrl_data->ctrl_dmac_wp =
        (u32*)((u64)ctrl_data->ctrl_dmac_buffer + CTRL_DMAC_WRITE_DMA_PAGES * PAGE_SIZE);
    mutex_unlock(&ctrl_data->ctrl_dmac_mutex);
    return 0;
}

static long sony_fpga_reg_update_completed(struct sony_fpga_ctrl* ctrl_data)
{
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_GIF) {
        if (process_status_area == NULL) {
            dev_err(CDEVICE(ctrl_data), "%s process_status_area is NULL\n", __FUNCTION__);
            return -EFAULT;
        }
        *process_status_area = vd_counter & 0xffff;
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        if (process_status_area_xpt == NULL) {
            dev_err(CDEVICE(ctrl_data), "%s process_status_area_xpt is NULL\n", __FUNCTION__);
            return -EFAULT;
        }
        *process_status_area_xpt = vd_counter & 0xffff;
    }
    return 0;
}

static long sony_fpga_video_file(struct sony_fpga_ctrl* ctrl_data, struct video_file_info __user* ptr, u32 len, uint rw)
{
    struct video_file_info video_info;
    void __iomem* dst_addr;
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 header1 = 0;
    u32 dst = 0;
    u64 current_page = 0;
    u64* current_descriptor = NULL;
    uint i = 0;
    uint command = 0;
    uint module_ctrl = 0;
    u32 module_status;
    int rc = 0;
    void* kmem = NULL;
    struct page **pages = NULL;
    uint nr_pages = 0;

    video_info.format = 0;
    video_info.dst_address = 0;
    video_info.buffer_address = NULL;
    video_info.buffer_size = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_GIF || CBUSNO(ctrl_data) != module_param_gif1_busno) {
        return 0;
    }
    if (sizeof(struct video_file_info) == len && copy_from_user(&video_info, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    // Descriptorを作る
    if (!ctrl_data->file_dmac_descriptor_virt_buffer) {
        printk(KERN_ERR "%s file_dmac_descriptor is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    rc = mutex_lock_interruptible(&ctrl_data->file_dmac_mutex);
    if (rc != 0) {
        printk(KERN_ERR "%s it can't be execute more than one\n", __FUNCTION__);
        return rc;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    if (module_param_debug_print > 0) {
        pr_debug("sony_fpga video_file dir = %d\n", rw);
        pr_debug("video_info:\n");
        pr_debug("format: %x\n", video_info.format);
        pr_debug("dst_address: %llx\n", video_info.dst_address);
        pr_debug("buffer_address: %p\n", video_info.buffer_address);
        pr_debug("buffer_size: %u\n", video_info.buffer_size);
    }
    if (rw == DIRECTION_READ) {
        module_ctrl = 1 << 4 | 1; // module enable : direction write
        dst_addr = &(fpga_reg->dmac_file.memory_write);
        command = 1;
    } else if (rw == DIRECTION_WRITE) {
        module_ctrl = 1; // module enable : direction read
        dst_addr = &(fpga_reg->dmac_file.memory_read);
        command = 1 << 1;
    } else {
        printk(KERN_ERR "%s direction unknown maybe bug\n", __FUNCTION__);
        mutex_unlock(&ctrl_data->file_dmac_mutex);
        return -EINVAL;
    }

    addr = &(fpga_reg->dmac_file.module_ctrl);
    IO_WRITE_32(module_ctrl, addr);

    //header1
    addr = &(fpga_reg->dmac_file.header1);
    header1 = 1 << 24;
    header1 |= video_info.format << 20;
    IO_WRITE_32(header1, addr);

    dst = video_info.dst_address >> 4;
    IO_WRITE_32(dst, dst_addr);

    rc = pin_user_page(ctrl_data, &kmem, video_info.buffer_address, video_info.buffer_size, &nr_pages, &pages);
    if (rc != 0) {
        dev_err(CDEVICE(ctrl_data), "%s : pin_user_page failed\n", __FUNCTION__);
        mutex_unlock(&ctrl_data->file_dmac_mutex);
        return rc;
    }
    current_page = PFN_PHYS(vmalloc_to_pfn(kmem));
    current_descriptor = ctrl_data->file_dmac_descriptor_virt_buffer;

    for (i = 0; i < video_info.buffer_size / PAGE_SIZE; i++) {
        *current_descriptor++ = current_page;
        current_page = PFN_PHYS(vmalloc_to_pfn(kmem + PAGE_SIZE * (i + 1)));
    }

    // 状態取得
    addr = &(fpga_reg->dmac_file.module_status);
    module_status = IO_READ_32(addr);
    if ((module_status & 1) == 0) {
        printk(KERN_DEBUG "%02x dmac file not ready %x\n", CBUSNO(ctrl_data), module_status);
        rc = -EAGAIN;
        goto finish;
    }

    // Kick
    pug_file_dma_start(fpga_reg, command);

    usleep_range(1, 2);

    // Poll
    for (i = 0; i < 1000 / 10; i++) {
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        if (module_param_debug_print > 0) {
            pr_debug("module_st %d\n", module_status);
        }
        if (module_status & 0b1100) {
            if (module_param_debug_print > 0) {
                pr_debug("video_file_dmac status ok\n");
            }
            break;
        } else {
            msleep(10);
        }
    }

    if (module_status & 0b1000) {
        dev_err(CDEVICE(ctrl_data), "dmac_file ModuleStatus Error\n");
        command = 1 << 4;
        addr = &(fpga_reg->dmac_file.module_ctrl);
        IO_WRITE_32(command, addr);
        wmb();
        command = 0;
        IO_WRITE_32(command, addr);
        wmb();
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        if (module_status & 0b1110) {
            dev_err(CDEVICE(ctrl_data), "dmac_file ModuleStatus Reset Error\n");
        }
        rc = -EFAULT;
    } else if (i == 1000 / 10) {
        rc = -ETIMEDOUT;
        printk(KERN_DEBUG "%s Timeout dir=%d\n", __FUNCTION__, rw);
    } else {
        rc = 0;
    }

    // Clear
    command = 0;
    addr = &(fpga_reg->file_dmac_control);
    IO_WRITE_32(command, addr);
    wmb();
finish:
    free_user_page(kmem, nr_pages, pages);
    mutex_unlock(&ctrl_data->file_dmac_mutex);

    return rc;
}

#ifndef GPU_DISABLE
static long sony_fpga_make_video_descriptor(struct sony_fpga_ctrl* ctrl_data,
                               struct video_dmac_region __user* ptr, u32 len)
{
    struct video_dmac_region region = {0};
    u64 current_page_address;
    uint i = 0;
    struct desc_set* target_desc_set = NULL; // TODO Descriptor構造は未定
    int gpu_page_offset;
    int gpu_page_num;
    int gpu_page_size;
    u32 pages_in_gpu_page;
    u32 offset_in_gpu_page;
    u32 size_with_offset;
    u32 page_cnt;
    u32 descriptor_offset;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct video_dmac_region) == len && copy_from_user(&region, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }


    if (ctrl_data->gd.pages == NULL) {
        dev_err(CDEVICE(ctrl_data), "%s gd.pages is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    gpu_page_size = get_gpu_page_size(ctrl_data->gd.pages);
    if (gpu_page_size == 0) {
        dev_err(CDEVICE(ctrl_data), "gpu_page_size = 0\n");
        return -EINVAL;
    }

    if (ctrl_data->video_desc_set == NULL) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc_set is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    gpu_page_offset = region.gpu_offset / gpu_page_size;
    offset_in_gpu_page = region.gpu_offset % gpu_page_size;
    size_with_offset   = region.size + offset_in_gpu_page;
    gpu_page_num       = ( size_with_offset % gpu_page_size) ?
                         ((size_with_offset / gpu_page_size) + 1) :
                         ( size_with_offset / gpu_page_size);

    if ((gpu_page_offset + gpu_page_num) > ctrl_data->gd.pages->entries) {
        dev_err(CDEVICE(ctrl_data), "%s : quit for going out of range\n", __FUNCTION__);
        return -EINVAL;
    }

    pages_in_gpu_page = gpu_page_size / PAGE_SIZE;
    page_cnt = region.size / PAGE_SIZE;

    if (assign_video_desc_set(ctrl_data, &region) < 0) {
        return -EFAULT;
    }

    /* 最初のregionはGPU PAGE境界に揃っていないかもしれない */
    region.size = gpu_page_size - offset_in_gpu_page;

    if (region.handle > VIDEO_DESC_SET_MAX) {
        dev_err(CDEVICE(ctrl_data), "%s region handle range error\n", __FUNCTION__);
        return -EFAULT;
    }
    target_desc_set = &ctrl_data->video_desc_set[region.handle];
    current_page_address = region.gpu_offset;
    descriptor_offset = 0;
    for (i = 0; i < gpu_page_num; i++) {
        bool last = (i + 1 == gpu_page_num) ? true : false;
        u32  written_page_cnt = 0;

        if (write_video_descriptor((target_desc_set->start_ptr + descriptor_offset), &region,
                             (ctrl_data->gd.pages->pages[gpu_page_offset+i]->physical_address + offset_in_gpu_page) ,
                             last)) {
            dev_err(CDEVICE(ctrl_data), "write descriptor failed\n");
        }

        written_page_cnt   = region.size / PAGE_SIZE;
        page_cnt          -= written_page_cnt;
        descriptor_offset += written_page_cnt;
        offset_in_gpu_page = 0;

        if (page_cnt >= pages_in_gpu_page) {
            region.size = gpu_page_size;
        } else {
            region.size = page_cnt * PAGE_SIZE;
        }
    }

    if (module_param_debug_print > 2) {
        printk(KERN_INFO "region_handle = %d\n", region.handle);
    }

    if (copy_to_user(ptr, &region, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    return 0;
}

static long sony_fpga_reset_video_descriptor(struct sony_fpga_ctrl* ctrl_data)
{
    if (ctrl_data == NULL ||
        ctrl_data->video_desc_set == NULL ||
        ctrl_data->video_descriptor_virt_addr == NULL) {
        printk(KERN_ERR "%s pointer is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    ctrl_data->free_video_descriptor = (struct dma_desc*)ctrl_data->video_descriptor_virt_addr;
    ctrl_data->free_video_descriptor_cnt = ctrl_data->video_descriptor_pages * PAGE_SIZE / sizeof(struct dma_desc);
    memset(ctrl_data->video_desc_set, 0x00, sizeof(struct desc_set) * VIDEO_DESC_SET_MAX);
    ctrl_data->current_desc_set_index = 0;

    return 0;
}

static long sony_fpga_set_video_descriptor(struct sony_fpga_ctrl* ctrl_data,
                               struct video_dmac_descriptor __user* ptr, u32 len)
{
    struct video_dmac_descriptor video_desc;
    void __iomem* addr;
    struct sony_fpga_register __iomem* reg;
    dma_addr_t descriptor_addr;

    video_desc.port = 0;
    video_desc.handle = 0;
    video_desc.ch = 0;
    video_desc.strip = 0;
    video_desc.direction = 0;
    video_desc.kind = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct video_dmac_descriptor) == len && copy_from_user(&video_desc, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (ctrl_data->video_desc_set == NULL) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc_set is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (video_desc.ch >= DMAC_CH_NUM) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc.ch range error\n", __FUNCTION__);
        return -EINVAL;
    } else if (video_desc.direction >= DIRECTION_NUM) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc.direction range error\n", __FUNCTION__);
        return -EINVAL;
    } else if (video_desc.strip >= VD_NUM) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc.strip range error\n", __FUNCTION__);
        return -EINVAL;
    } else if (video_desc.port >= DMA_PORT_NUM) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc.port range error\n", __FUNCTION__);
        return -EINVAL;
    } else if (video_desc.handle >= VIDEO_DESC_SET_MAX) {
        dev_err(CDEVICE(ctrl_data), "%s video_desc.handle range error\n", __FUNCTION__);
        return -EINVAL;
    }

    reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    if (reg == NULL) {
        dev_err(CDEVICE(ctrl_data), "%s bar area is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    addr = &(reg->dmac_media[video_desc.port].desc_addr[video_desc.ch][video_desc.direction][video_desc.strip]);

    if (module_param_debug_print > 2) {
        printk(KERN_INFO "port:%d handle:%d ch:%d strip:%d direction:%d\n", video_desc.port, video_desc.handle, video_desc.ch, video_desc.strip, video_desc.direction);
    }

    descriptor_addr = virt_to_phys(ctrl_data->video_desc_set[video_desc.handle].start_ptr);
    IO_WRITE_64(descriptor_addr, addr);
    return 0;
}
#endif

static long sony_fpga_set_multi_cast_address(struct sony_fpga_ctrl* ctrl_data, struct multicast_info __user* ptr, u32 len)
{
    struct multicast_info multicast;
    void __iomem* addr;
    struct sony_fpga_register __iomem* reg;

    //memset(&multicast, 0x00, sizeof(multicast));
    multicast.number = 0;
    multicast.address = 0;
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct multicast_info) == len && copy_from_user(&multicast, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    if (reg == NULL) {
        dev_err(CDEVICE(ctrl_data), "%s bar area is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (multicast.number) {
    case 1:
        addr = &(reg->multi_cast1_write_adrs);
        break;
    case 2:
        addr = &(reg->multi_cast2_write_adrs);
        break;
    case 3:
        addr = &(reg->multi_cast3_write_adrs);
        break;
    case 4:
        addr = &(reg->multi_cast4_write_adrs);
        break;
    case 5:
        addr = &(reg->multi_cast5_write_adrs);
        break;
    case 6:
        addr = &(reg->multi_cast6_write_adrs);
        break;
    case 7:
        addr = &(reg->multi_cast7_write_adrs);
        break;
    case 8:
        addr = &(reg->multi_cast8_write_adrs);
        break;
    default:
        return -EINVAL;
    }

    IO_WRITE_64(multicast.address, addr);

    return 0;
}

#ifndef GPU_DISABLE
static long sony_fpga_pin_gpu_memory(struct sony_fpga_ctrl* ctrl, struct gpu_mem_info __user* ptr, u32 len)
{
    long ret = 0;
    struct gpu_mem_info meminfo;
    meminfo.address = NULL;
    meminfo.size = 0;

    if (ctrl == NULL) {
        printk(KERN_ERR "%s ctrl is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct gpu_mem_info) == len && copy_from_user(&meminfo, ptr, len)) {
        dev_err(CDEVICE(ctrl), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    ret = kmd_pin_memory(ctrl, meminfo.address, meminfo.size);

    return ret;
}

/* parameterのチェックはしない。既にチェック済であることが前提 */
static long write_video_descriptor(struct dma_desc* desc_head, struct video_dmac_region* param, dma_addr_t target_base_addr, bool last)
{
    struct dma_desc* desc = desc_head;
    int i = 0;
    dma_addr_t target_addr = target_base_addr;
    if (desc == NULL) {
        printk(KERN_DEBUG "desc_head is NULL!!\n");
        return -EFAULT;
    }

    for (i = 0; i < (param->size / PAGE_SIZE); i++) {
        desc->pcie_addr  = target_addr;
        desc->local_addr = 0xeeeeeeee;
        desc->terminator = 0;
        target_addr += PAGE_SIZE;
        desc++;
    }
    if (last) {
        desc--;
        desc->terminator = 1;
    }

    wmb();
    return 0;
}

static long assign_video_desc_set(struct sony_fpga_ctrl* ctrl_data, struct video_dmac_region* region)
{
    struct desc_set* target_desc_set = &ctrl_data->video_desc_set[ctrl_data->current_desc_set_index];
    u32 page_cnt = region->size / PAGE_SIZE;
    bool add_node = false;
    u32 modify_page;
    const u32 align = 0x200 / sizeof(struct dma_desc);

    /* 初回割当 */
    if (!target_desc_set->start_ptr) {
        /* 1 descriptorあたり 1 CPU page の転送量となる */
        if (ctrl_data->free_video_descriptor_cnt < page_cnt) {
            dev_err(CDEVICE(ctrl_data), "%s : not enough descriptor area\n", __FUNCTION__);
//フラグメントを修復する契機
            return -EFAULT;
        }
        add_node = true;
        /* printk("1st assign, desc_set=%d page_cnt=%d\n", region.desc_set, page_cnt); */
    }

    // Descriptor読み出し開始位置は0x200hのアライメントが必要
    modify_page = (page_cnt + align - 1) & ~(align - 1);
    target_desc_set->cnt = modify_page;
    if (add_node) {
        target_desc_set->start_ptr = ctrl_data->free_video_descriptor;
        ctrl_data->free_video_descriptor = (target_desc_set->start_ptr + modify_page);
        ctrl_data->free_video_descriptor_cnt -= modify_page;
    }
    region->handle = ((u64)target_desc_set - (u64)ctrl_data->video_desc_set) / sizeof(struct desc_set);

    ctrl_data->current_desc_set_index++;
    return 0;
}

static int get_gpu_page_size(const nvidia_p2p_page_table_t* table)
{
    int gpu_page_size;
    switch (table->page_size)
    {
    case NVIDIA_P2P_PAGE_SIZE_4KB:
        gpu_page_size =   4*1024;
        break;
    case NVIDIA_P2P_PAGE_SIZE_64KB:
        gpu_page_size =  64*1024;
        break;
    case NVIDIA_P2P_PAGE_SIZE_128KB:
        gpu_page_size = 128*1024;
        break;
    default:
        gpu_page_size = 0;
    }
    return gpu_page_size;
}

// Video DMA開始
void video_dmac_start_func(struct work_struct* work)
{
    u32 value = 0;
    u32 i;
    void __iomem* addr;
    struct sony_fpga_ctrl *ctrl = container_of(work, struct sony_fpga_ctrl, video_dmac_start_work.work);
    struct sony_fpga_register __iomem* fpga_reg;
    ktime_t wait;

    if (work == NULL) {
        printk(KERN_ERR "video_dmac_start func initialize error\n");
        return;
    }
    if (!IS_GIF_VIDEO(ctrl)) {
        printk(KERN_ERR "video_dmac_start fpga type error\n");
        mutex_unlock(&ctrl->video_dmac_mutex);
        return;
    }

    pr_debug("video_dmac_start_func %d\n", smp_processor_id());
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl->bar[FPGA_CTRL_BAR];

    value = 1;
    addr = &(fpga_reg->memory_reset);
    IO_WRITE_32(value, addr);
    value = 0;
    IO_WRITE_32(value, addr);

    addr = &(fpga_reg->memory_status);
    wait = ktime_set(1, 0);
    for (i = 0; i < hbm_reset_wait_cnt; i++) {
        u32 memory_status = IO_READ_32(addr);
        if ((memory_status & 1) == 1) {
            break;
        }
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
    }
    if (i == hbm_reset_wait_cnt) {
        printk(KERN_ERR "hbm2 reset release Timeout %d\n", smp_processor_id());
    } else {
        pr_debug("memory_status reset release %d %d\n", i, smp_processor_id());
    }

    value = SONY_FPGA_ENABLE;
    addr = &(fpga_reg->video_dmac_control);
    IO_WRITE_32(value, addr);
    wmb();
    ctrl->video_dmac_control = value;

    addr = &(fpga_reg->irq_vd_count);
    value = IO_READ_32(addr);
    printk(KERN_DEBUG "video dmac enable int_count = %d\n", value);

    mutex_unlock(&ctrl->video_dmac_mutex);
    return;
}

static long sony_fpga_set_video_dmac_control(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 value = 0;
    int rc;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (!IS_GIF_VIDEO(ctrl_data)) {
        return 0;
    }

    if (sizeof(u32) == len && get_user(value, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    rc = mutex_lock_interruptible(&ctrl_data->video_dmac_mutex);
    if (rc != 0) {
        return rc;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    if (value == SONY_FPGA_ENABLE) {
        schedule_delayed_work(&ctrl_data->video_dmac_start_work, msecs_to_jiffies(100));
        return 0;
    }

    addr = &(fpga_reg->video_dmac_control);
    IO_WRITE_32(value, addr);
    wmb();
    ctrl_data->video_dmac_control = value;

    msleep(video_dmac_stop_wait);

    mutex_unlock(&ctrl_data->video_dmac_mutex);
    return 0;
}

static long sony_fpga_set_video_dmac_channel(struct sony_fpga_ctrl* ctrl_data, struct video_dmac_channel_setting __user* ptr, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    struct video_dmac_channel_setting setting = {0};
    u32 value = 0;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (!IS_GIF_VIDEO(ctrl_data)) {
        return 0;
    }

    if (sizeof(setting) == len && copy_from_user(&setting, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    value |= (setting.write_port == SONY_FPGA_DISABLE) ? 1 : 0;
    value |= (setting.read_port == SONY_FPGA_DISABLE) ? 1 << 16 : 0;
    if (setting.ch < 1 || setting.ch >= DMA_PORT_NUM) {
        return -EINVAL;
    }
    addr = &(fpga_reg->dmac_media[setting.ch-1].dma_disable);
    IO_WRITE_32(value, addr);
    wmb();
    return 0;
}
#endif

static long sony_fpga_wait_vd(struct sony_fpga_ctrl* ctrl_data)
{
    u32 counter;
    counter = int_counter;
    vd_wait_event_interruptible(vd_wait_queue, int_counter != counter);
    return 0;
}

static long sony_fpga_wait_vd_info(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 counter = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    counter = int_counter;
    vd_wait_event_interruptible(vd_wait_queue, int_counter != counter);
    counter = int_counter;
    if (sizeof(counter) == len && put_user(counter, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    return 0;
}

static long sony_fpga_wait_vd_info_timeout(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 counter    = 0;
    long ret       = 0;
    u32 timeout_ms = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(u32) == len && get_user(timeout_ms, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    counter = int_counter;
    ret = vd_wait_event_interruptible_timeout(vd_wait_queue, int_counter != counter, timeout_ms);

    // 正常に割り込んだ場合、残り時間が返る
    if (ret > 0) {
        counter = int_counter;
        if (sizeof(counter) == len && put_user(counter, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
     // Timeout　
    } else if (ret == 0) {
        counter = -1;
        if (sizeof(counter) == len && put_user(counter, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        dev_err(CDEVICE(ctrl_data), "%s : recv timeout\n", __FUNCTION__);
        return -ETIME;
      // Signal受信
    } else {
        dev_err(CDEVICE(ctrl_data), "%s : recv signal\n", __FUNCTION__);
    }

    return 0;
}

#define IMPLEMENT_REGSET_FUNCTION(name, type) \
static long sony_fpga_set_##name(struct sony_fpga_ctrl* ctrl_data, type __user* ptr, u32 len) \
{ \
    struct sony_fpga_register __iomem* fpga_reg; \
    void __iomem* addr; \
    type value = 0; \
\
    if (ctrl_data == NULL) { \
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__); \
        return -EFAULT; \
    } \
    if (sizeof(value) == len && get_user(value, ptr)) { \
        dev_err(&ctrl_data->pci_dev->dev, "%s : get_user failed\n", __FUNCTION__); \
        return -EFAULT; \
    } \
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR]; \
    addr = &(fpga_reg->name); \
    IO_WRITE_32(value, addr); \
    wmb(); \
    return 0; \
}

IMPLEMENT_REGSET_FUNCTION(irq_vd_control, u32)
IMPLEMENT_REGSET_FUNCTION(ctrl_master_mode, u32)
IMPLEMENT_REGSET_FUNCTION(ctrl_dmac_control, u32)
IMPLEMENT_REGSET_FUNCTION(multi_cast_start, u32)

static long sony_fpga_ctrl_dmac_ignore_address(struct sony_fpga_ctrl* ctrl_data, struct ctrl_dmac_ignore_list __user* ptr, u32 len)
{
    struct ctrl_dmac_ignore_list list;
    list.list_addr = NULL;
    list.size = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(struct ctrl_dmac_ignore_list) == len && copy_from_user(&list, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    if ((IGNORE_LIST_MAX - ignore_list.block_cnt) > list.size) {
        if (copy_from_user(&(ignore_list.data[ignore_list.block_cnt]), (void __user*)list.list_addr, list.size)) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        ignore_list.block_cnt += list.size / sizeof(u32);
    } else {
        dev_err(CDEVICE(ctrl_data), "%s : ctrl_dmac_ignore_list not enough space\n", __FUNCTION__);
        return -EFAULT;
    }
    return 0;
}

static long sony_fpga_ctrl_dmac_ignore_list_clear(struct sony_fpga_ctrl* ctrl_data)
{
    ignore_list.block_cnt = 0;
    return 0;
}

static long sony_fpga_cmp_test_ctrl(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 ctrl = 0;
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(u32) == len && get_user(ctrl, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->cmp_test_ctrl);
    if (ctrl == CTRL_DMAC_WRITE_NORMAL && module_param_board_revision == 0) {
        ctrl = CTRL_DMAC_WRITE_INTEL;
    }
    IO_WRITE_32(ctrl, addr);
    ctrl_data->cmp_test_ctrl = ctrl;
    wmb();

    return 0;
}

static long sony_fpga_refsync_mon_clear(struct sony_fpga_ctrl* ctrl_data)
{
    u32 clear = 0x01;
    void __iomem* addr;
    struct ref_mgr_reg __iomem* reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    reg = (struct ref_mgr_reg __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + REF_MGR_START_OFFSET);
    addr = &(reg->debug_mon_cntl);
    IO_WRITE_32(clear, addr);
    wmb();

    pr_debug("refsync_reset %02x\n", CBUSNO(ctrl_data));
    return 0;
}

int module_param_video_format_wait = 30000;
module_param(module_param_video_format_wait, int, S_IRUGO | S_IWUSR);

static long sony_fpga_set_video_format(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 video_format = 0;
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (sizeof(u32) == len && get_user(video_format, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    addr = &(fpga_reg->video_format);
    IO_WRITE_32(video_format, addr);
    ctrl_data->video_format = video_format;

    return 0;
}

static long sony_fpga_video_format_interrupt(struct sony_fpga_ctrl* ctrl_data)
{
    u32 data = 0;
    void __iomem* addr;
    uint i = 0;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 status;
    u32 fpga_kind;
    u32 video_format;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    fpga_kind = FPGA_KIND(ctrl_data->fpga_uniq_id);
    switch (fpga_kind) {
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_BIM:
    case FPGA_KIND_SWR: // VPR-149の場合のみ
    case FPGA_KIND_XPT:
    case FPGA_KIND_SDI:
    case FPGA_KIND_SIS:
        break;
    default:
        // 代表FPGAでない場合はなにもしない
        return 0;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    switch (fpga_kind) {
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_SWR:
        video_format = ctrl_data->video_format & 0x1ffff;
        if ((video_format & 0xf) < 4 ||  // 4K Progressive以外は通す
            (video_format == 0x2a44) ||  // 4K PureP/25 (25Hz 追加)
            (video_format == 0x3a44) ||  // 4K PureP/29-6G
            (video_format == 0x0a44)) {  // 4K PureP/23-6G
            addr = &(fpga_reg->clk_pll_lock_status);
            for (i = 0; i < 3; i++) {
                status = IO_READ_32(addr);
                pr_debug("pll_lock_status %x\n", status);
                if ((status & ~5) == 2) {
                    break;
                }
                msleep(100);
            }
            if (i == 3) {
                dev_err(CDEVICE(ctrl_data), "clk_pll_unlock\n");
            }
        }
        break;
    default:
        // DebugSDIは未対応
        // BIM FPGAも未対応（今後対応の可能性あり）
        break;
    }

    data = 1; // 割込要因 VideoFormat
    addr = &(fpga_reg->irq_nios);
    IO_WRITE_32(data, addr);

    for (i = 0; i < 3; i++) {
        data = 1; // irq_req_lock通知
        addr = &(fpga_reg->irq_ref_lock);
        IO_WRITE_32(data, addr);

        usleep_range(module_param_video_format_wait, module_param_video_format_wait + 10);

        addr = &(fpga_reg->irq_nios);
        data = IO_READ_32(addr);
        if (data == 0) {
            mutex_unlock(&ctrl_data->nios_mutex);
            pr_debug("nios_mutex unlock %02x\n", CBUSNO(ctrl_data));
            return 0;
        } else {
            printk(KERN_DEBUG "%02x Warning NIOS IRQ\n", CBUSNO(ctrl_data));
        }
    }
    mutex_unlock(&ctrl_data->nios_mutex);
    pr_debug("nios_mutex unlock %02x\n", CBUSNO(ctrl_data));
    return -ETIME;
}

static long sony_fpga_set_sdi_out_ch(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 out_enable = 0;
    u32 data = 0;
    void __iomem* addr;
    uint i = 0;
    struct sony_fpga_register __iomem* fpga_reg;
    int rc;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SDI &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SIS) {
        return 0;
    }

    if (sizeof(u32) == len && get_user(out_enable, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    pr_debug("mutex lock %02x\n", CBUSNO(ctrl_data));
    rc = mutex_lock_interruptible(&ctrl_data->nios_mutex);
    if (rc != 0) {
        pr_err("sony_fpga_set_sdi_out_ch mutex_lock error");
        return rc;
    }
    pr_debug("mutex lock %02x\n", CBUSNO(ctrl_data));
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    addr = &(fpga_reg->multipurpose);
    IO_WRITE_32(out_enable, addr);

    data = 0x100; // 割込要因 multipurpose
    addr = &(fpga_reg->irq_nios);
    IO_WRITE_32(data, addr);

    data = 1; // irq_req_lock通知
    addr = &(fpga_reg->irq_ref_lock);
    IO_WRITE_32(data, addr);

    msleep(40);
    for (i = 0; i < 3; i++) {
        addr = &(fpga_reg->irq_nios);
        data = IO_READ_32(addr);
        if (data == 0) {
            u32 multipurpose;
            addr = &(fpga_reg->multipurpose);
            multipurpose = IO_READ_32(addr);
            if (multipurpose == 0x80000000) {
                multipurpose = 0;
                IO_WRITE_32(multipurpose, addr);
                mutex_unlock(&ctrl_data->nios_mutex);
                pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
                return 0;
            } else {
                printk(KERN_DEBUG "%02x multipurpose not 0x80000000\n", CBUSNO(ctrl_data));
                mutex_unlock(&ctrl_data->nios_mutex);
                pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
                return -EFAULT;
            }
        } else {
            printk(KERN_DEBUG "SDI OUT CH Warning NIOS IRQ\n");
            data = 1; // irq_req_lock通知
            addr = &(fpga_reg->irq_ref_lock);
            IO_WRITE_32(data, addr);
            msleep(20);
        }
    }
    printk(KERN_DEBUG "SDI OUT CH Error NIOS IRQ\n");
    mutex_unlock(&ctrl_data->nios_mutex);
    pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
    return -ETIME;
}

static long sony_fpga_ref_lock(struct sony_fpga_ctrl* ctrl_data)
{
    u32 data = 0;
    void __iomem* addr;
    uint i = 0;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 int_factor = 0x1000;
    u32 fpga_kind;
    int rc;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    fpga_kind = FPGA_KIND(ctrl_data->fpga_uniq_id);
    switch (fpga_kind) {
    case FPGA_KIND_SDI:
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_BIM:
    case FPGA_KIND_XPT:
    case FPGA_KIND_SWR:
    case FPGA_KIND_SIS:
        break;
    default:
        return 0;
    }

    pr_debug("nios_mutex lock %02x\n", CBUSNO(ctrl_data));
    rc = mutex_lock_interruptible(&ctrl_data->nios_mutex);
    if (rc != 0) {
        pr_err("sony_fpga_ref_lcok mutex_lock error %02x\n", CBUSNO(ctrl_data));
        return rc;
    }
    pr_debug("nios_mutex lock %02x\n", CBUSNO(ctrl_data));

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->irq_nios);
    IO_WRITE_32(int_factor, addr);

    data = 1; // irq_req_lock通知
    addr = &(fpga_reg->irq_ref_lock);
    IO_WRITE_32(data, addr);

    if (fpga_kind == FPGA_KIND_GIF || fpga_kind == FPGA_KIND_GIF2) {
        ktime_t wait;
        wait = ktime_set(0, 1000000);
        for (i = 0; i < hbm_reset_wait_cnt * 1000; i++) {
            u32 memory_status = 0;

            addr = &(fpga_reg->memory_status);
            memory_status = IO_READ_32(addr);
            if (memory_status & 1) {
            } else {
                pr_debug("memory_status reset wait %d\n", i);
                break;
            }
            set_current_state(TASK_INTERRUPTIBLE);
            schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
        }
        if (i == hbm_reset_wait_cnt) {
            printk(KERN_ERR "HBM Reset Timeout\n");
        }
    }
    msleep(40);
    for (i = 0; i < 3; i++) {
        addr = &(fpga_reg->irq_nios);
        data = IO_READ_32(addr);
        if (data == 0) {
            mutex_unlock(&ctrl_data->nios_mutex);
            pr_debug("nios_mutex unlock %02x\n", CBUSNO(ctrl_data));
            return 0;
        } else {
            printk(KERN_DEBUG "%02x RefLock Warning NIOS IRQ\n", CBUSNO(ctrl_data));
            data = 1; // irq_req_lock通知
            addr = &(fpga_reg->irq_ref_lock);
            IO_WRITE_32(data, addr);
            msleep(20);
        }
    }

    mutex_unlock(&ctrl_data->nios_mutex);
    pr_debug("nios_mutex unlock %02x\n", CBUSNO(ctrl_data));
    printk(KERN_DEBUG "%02x VideoFormat Error NIOS IRQ\n", CBUSNO(ctrl_data));
    return -EFAULT;
}

static long sony_fpga_audio_mute(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 mute = 0;
    void __iomem* addr;
    struct ref_mgr_reg __iomem* reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SDI &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_BIM &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SIS) {
        return 0;
    }

    if (sizeof(u32) == len && get_user(mute, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    reg = (struct ref_mgr_reg __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + REF_MGR_START_OFFSET);
    addr = &(reg->misc_ctrl);
    IO_WRITE_32(mute, addr);

    return 0;
}

static long sony_fpga_audio_file(struct sony_fpga_ctrl* ctrl_data, struct audio_file_info __user* arg, u32 len, uint rw)
{
    void __iomem* addr;
    void __iomem* dst_addr;
    struct sony_fpga_register __iomem* fpga_reg;
    struct audio_file_info param;
    u64 current_page = 0;
    u64* current_descriptor = NULL;
    u32 file_dmac_ctrl;
    u32 module_ctrl;
    u32 module_status;
    u32 header1 = 2 << 24;  // Type Audio
    u32 header2 = 0;
    u32 header3 = 0;
    int rc;
    uint i;
    u32 nr_pages = 0;
    void* kmem = NULL;
    struct page **pages = NULL;
    u32 video_format;
    u32 cond1;
    u32 cond2;

    memset(&param.head, 0x00, sizeof(param.head));
    param.target_addr = 0;
    param.next_read = 0;
    param.buffer_address = NULL;
    param.buffer_size = 0;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SDI &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_BIM &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SIS) {
        return 0;
    }

    if (sizeof(param) == len && copy_from_user(&param, arg, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    // Descriptorを作る
    if (!ctrl_data->file_dmac_descriptor_virt_buffer) {
        return -EFAULT;
    }

    rc = mutex_lock_interruptible(&ctrl_data->file_dmac_mutex);
    if (rc != 0) {
        return rc;
    }

    // Header情報の設定
    header1 |= param.head.sample_status << 18 | param.head.endian << 17 | param.head.ch_assign;
    header2 = param.head.payload_size;
    header3 = param.head.sample_size;

    // 4K PureP 時(payload size > 48KB or video_format が PureP で判断)は header1 に 4K PureP 設定
    // 48KB 超え判定は payload size で行う。(smple size は 4k 程度の小さいサイズになりうるため)
    // 矛盾する場合は debug print
    video_format = ctrl_data->video_format & 0x1ffff;
    cond1 = (header2 > 48*1024); // check payload size
    cond2 = ((video_format == 0x2a44) ||  // 4K PureP/25 (25Hz 追加)
             (video_format == 0x3a44) ||  // 4K PureP/29-6G
             (video_format == 0x0a44));   // 4K PureP/23-6G
    if (cond1 || cond2) {
        // header1[23:20] : FM Video/AudioのFormat, 0h:4K PureP以外Format Audio, 1h: 4K PureP Audio
        header1 |= 1 << 20; // format
    }
    if (cond1 != cond2) {
        pr_debug("*** Warning ***: payload size = %x, format = %x\n", header2, video_format);
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    if (rw == DIRECTION_READ) {
        module_ctrl = 1 << 4 | 1; // module enable : direction write
        dst_addr = &(fpga_reg->dmac_file.memory_write);
        file_dmac_ctrl = 1;
        addr = &(fpga_reg->dmac_file.memory_write_next);
        IO_WRITE_32(param.next_read, addr);
    } else if (rw == DIRECTION_WRITE) {
        module_ctrl = 1; // module enable : direction read
        dst_addr = &(fpga_reg->dmac_file.memory_read);
        file_dmac_ctrl = 1 << 1;
    } else {
        mutex_unlock(&ctrl_data->file_dmac_mutex);
        return -EINVAL;
    }

    if (module_param_debug_print > 1) {
        pr_debug("sony_fpga audio_file dir = %d\n", rw);
        pr_debug("audio_info:\n");
        pr_debug("header1: %x\n", header1);
        pr_debug("header2: %x\n", header2);
        pr_debug("header3: %x\n", header3);
        pr_debug("dst_addr: %llx\n", param.target_addr);
    }

    addr = &(fpga_reg->dmac_file.module_ctrl);
    IO_WRITE_32(module_ctrl, addr);

    addr = &(fpga_reg->dmac_file.header1);
    IO_WRITE_32(header1, addr);

    addr = &(fpga_reg->dmac_file.header2);
    IO_WRITE_32(header2, addr);

    addr = &(fpga_reg->dmac_file.header3);
    IO_WRITE_32(header3, addr);

    IO_WRITE_32(param.target_addr, dst_addr);

    // Descriptorの設定
    rc = pin_user_page(ctrl_data, &kmem, param.buffer_address, param.buffer_size, &nr_pages, &pages);
    if (rc != 0) {
        mutex_unlock(&ctrl_data->file_dmac_mutex);
        return rc;
    }

    current_page = PFN_PHYS(vmalloc_to_pfn(kmem));
    current_descriptor = ctrl_data->file_dmac_descriptor_virt_buffer;
    for (i = 0; i < param.buffer_size / PAGE_SIZE; i++) {
        *current_descriptor++ = current_page;
        current_page = PFN_PHYS(vmalloc_to_pfn(kmem + PAGE_SIZE * (i + 1)));
    }

    // 状態取得
    addr = &(fpga_reg->dmac_file.module_status);
    module_status = IO_READ_32(addr);
    if ((module_status & 1) == 0) {
        printk(KERN_DEBUG "%02x dmac file not ready %x\n", CBUSNO(ctrl_data), module_status);
        rc = -EAGAIN;
        goto finish;
    }

    // Kick
    pug_file_dma_start(fpga_reg, file_dmac_ctrl);

    // Poll
    for (i = 0; i < 1000000 / module_param_afm_wait; i++) {
        usleep_range(module_param_afm_wait, module_param_afm_wait + 10);
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        if (module_param_debug_print > 1) {
            pr_debug("module_st %d\n", module_status);
        }
        if (module_status & 0b1100) {
            if (module_param_debug_print > 1) {
                pr_debug("audio_file_dmac status ok\n");
            }
            break;
        }
    }

    if (module_status & 0b1000) {
        u32 module_ctrl = 0;
        printk(KERN_DEBUG "%02x dmac_file ModuleStatus Error\n", CBUSNO(ctrl_data));
        module_ctrl = 1 << 4;
        addr = &(fpga_reg->dmac_file.module_ctrl);
        IO_WRITE_32(module_ctrl, addr);
        wmb();
        module_ctrl = 0;
        IO_WRITE_32(module_ctrl, addr);
        wmb();
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        if (module_status & 0b1110) {
            printk(KERN_DEBUG "%02x dmac_file ModuleStatus Reset Error\n", CBUSNO(ctrl_data));
        }
        rc = -EFAULT;
    } else if (i == 1000000 / module_param_afm_wait) {
        rc = -ETIMEDOUT;
        printk(KERN_DEBUG "%s Timeout dir=%d\n", __FUNCTION__, rw);
    } else {
        rc = 0;
    }

    // Clear
    file_dmac_ctrl = 0;
    addr = &(fpga_reg->file_dmac_control);
    IO_WRITE_32(file_dmac_ctrl, addr);
    wmb();

finish:
    free_user_page(kmem, nr_pages, pages);
    mutex_unlock(&ctrl_data->file_dmac_mutex);

    return rc;
}

static long sony_fpga_slavedata_file_write(struct sony_fpga_ctrl* ctrl_data, struct slavedata_write_info __user* arg, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    struct slavedata_write_info param;
    u64 current_page = 0;
    u64* current_descriptor = NULL;
    u32 file_dmac_ctrl;
    u32 module_ctrl;
    u32 module_status;
    u32 header1 = 0;  // Type Data
    int rc;
    u16 crc;
    uint i;
    u32 nr_pages = 0;
    void* kmem = NULL;
    struct page **pages = NULL;
    u32 fpga_kind;

    param.block_num = 0;
    param.buffer_address = NULL;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    fpga_kind = FPGA_KIND(ctrl_data->fpga_uniq_id);
    switch (fpga_kind) {
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_BIM:
    case FPGA_KIND_SWR:
    case FPGA_KIND_XPT:
    case FPGA_KIND_SDI:
    case FPGA_KIND_SIS:
        break;
    default:
        return 0;
    }

    if (sizeof(param) == len && copy_from_user(&param, arg, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    // Descriptorを作る
    if (!ctrl_data->file_dmac_descriptor_virt_buffer) {
        return -EFAULT;
    }

#if 0
    //MUTEX取得はファイル全体でやる予定。

    rc = mutex_lock_interruptible(&ctrl_data->file_dmac_mutex);
    if (rc != 0) {
        return rc;
    }
#endif

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    module_ctrl = 1; // module enable : direction read
    file_dmac_ctrl = 1 << 1;

    addr = &(fpga_reg->dmac_file.module_ctrl);
    IO_WRITE_32(module_ctrl, addr);

    addr = &(fpga_reg->dmac_file.header1);
    IO_WRITE_32(header1, addr);

    addr = &(fpga_reg->dmac_file.memory_read);
    IO_WRITE_32(0, addr);

    // 現在のブロックを設定
    addr = &(fpga_reg->dmac_file.file_block);
    IO_WRITE_32(param.block_num, addr);

    // Descriptorの設定
    rc = pin_user_page(ctrl_data, &kmem, param.buffer_address, CONFIG_DATA_BLOCK_SIZE, &nr_pages, &pages);
    if (rc != 0) {
        return rc;
    }

    // CRCを設定
    crc = crc16(0, kmem, CONFIG_DATA_BLOCK_SIZE);
    addr = &(fpga_reg->dmac_file.checksum);
    IO_WRITE_32((u32)crc, addr);

    current_page = PFN_PHYS(vmalloc_to_pfn(kmem));
    current_descriptor = ctrl_data->file_dmac_descriptor_virt_buffer;
    for (i = 0; i < CONFIG_DATA_BLOCK_SIZE / PAGE_SIZE; i++) {
        *current_descriptor++ = current_page;
        current_page = PFN_PHYS(vmalloc_to_pfn(kmem + PAGE_SIZE * (i + 1)));
    }

    // 状態取得
    addr = &(fpga_reg->dmac_file.module_status);
    module_status = IO_READ_32(addr);
    if ((module_status & 1) == 0) {
        dev_err(CDEVICE(ctrl_data), "dmac file not ready %x\n", module_status);
        rc = -EAGAIN;
        goto finish;
    }

    // Kick
    pug_file_dma_start(fpga_reg, file_dmac_ctrl);

    usleep_range(1, 2);

    // Poll
    for (i = 0; i < 1000 / 10; i++) {
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        printk(KERN_INFO "module_st %d\n", module_status);
        if (module_status & 0b1100) {
            break;
        } else {
            msleep(10);
        }
    }

    if (module_status & 0b1000) {
        u32 module_ctrl = 0;
        dev_err(CDEVICE(ctrl_data), "dmac_file ModuleStatus Error\n");
        module_ctrl = 1 << 4;
        addr = &(fpga_reg->dmac_file.module_ctrl);
        IO_WRITE_32(module_ctrl, addr);
        wmb();
        module_ctrl = 0;
       IO_WRITE_32(module_ctrl, addr);
        wmb();
        addr = &(fpga_reg->dmac_file.module_status);
        module_status = IO_READ_32(addr);
        if (module_status & 0b1110) {
            dev_err(CDEVICE(ctrl_data), "dmac_file ModuleStatus Reset Error\n");
        }
        rc = -EFAULT;
    } else if (i == 1000 / 10) {
        rc = -ETIMEDOUT;
        printk(KERN_DEBUG "%s Timeout\n", __FUNCTION__);
    } else {
        rc = 0;
        addr = &(fpga_reg->irq_nios); // Data Update割込要因
        IO_WRITE_32(0x00010000, addr);
        // slave_update処理はアプリ非稼動状況化でのみ動作するため
        // 本件が唯一のNIOS割込となるため排他制御は割愛する
        addr = &(fpga_reg->irq_ref_lock);
        IO_WRITE_32(1, addr); // irq_req_lock通知
    }

    // Clear
    file_dmac_ctrl = 0;
    addr = &(fpga_reg->file_dmac_control);
    IO_WRITE_32(file_dmac_ctrl, addr);
    wmb();

finish:
    free_user_page(kmem, nr_pages, pages);

    return rc;
}

static long sony_fpga_slavedata_update_start(struct sony_fpga_ctrl* ctrl_data, struct slavedata_start_param __user* arg, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    struct slavedata_start_param param = {0};
    u32 file_install;
    long ret = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    if (sizeof(param) == len && copy_from_user(&param, arg, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    ret = mutex_lock_interruptible(&ctrl_data->file_dmac_mutex);
    if (ret != 0) {
        return ret;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->dmac_file.file_install);
    file_install = IO_READ_32(addr);
    if (file_install != 1) {
        dev_err(CDEVICE(ctrl_data), "%s file_install(0x230) = 0x%08X != 1\n", __FUNCTION__, file_install); // 0x230);
        return -EBUSY;
    }

    addr = &(fpga_reg->dmac_file.file_target);
    IO_WRITE_32(param.file_target << 16 | param.block_count, addr);
    return ret;
}

static long sony_fpga_slavedata_update_progress(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 file_install;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->dmac_file.file_install);
    file_install = IO_READ_32(addr);
    if (sizeof(file_install) == len && put_user(file_install, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    return 0;
}

static long sony_fpga_slavedata_update_error_clear(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 file_target;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    // <<<H-2>>>
    addr = &(fpga_reg->dmac_file.file_target);

    // file_target status print before clear
    file_target = IO_READ_32(addr);
    dev_err(CDEVICE(ctrl_data), "%s update fail:file_target = 0x%08X\n", __FUNCTION__, file_target);
    if (sizeof(file_target) == len && put_user(file_target, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    // file_target status clear
    IO_WRITE_32(0x000C0000, addr); // file_target[19:16] = 0xC:Status Clear
                                   // file_target[15:00] = 0x0000:TBN=0

    // <<<H-4>>>
    addr = &(fpga_reg->irq_nios); // Data Update割込要因
    IO_WRITE_32(0x00010000, addr);
    addr = &(fpga_reg->irq_ref_lock);
    IO_WRITE_32(1, addr); // irq_req_lock通知

    return 0;
}

static long sony_fpga_slavedata_update_finish(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 reg_data = 0;
    u32 reg_clear = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl is NULL\n", __FUNCTION__);
        return -EFAULT;
    }
    // レジスタがクリアされていることを確認
    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];

    addr = &(fpga_reg->irq_nios);
    reg_data = IO_READ_32(addr);
    if (reg_data != 0) {
        reg_clear |= REGCLR_STATUS_IRQ_NIOS;
    }

    addr = &(fpga_reg->dmac_file.file_target);
    reg_data = IO_READ_32(addr);
    if (reg_data != 0) {
        reg_clear |= REGCLR_STATUS_FILE_TARGET;
    }

    addr = &(fpga_reg->dmac_file.file_block);
    reg_data = IO_READ_32(addr);
    if (reg_data != 0) {
        reg_clear |= REGCLR_STATUS_FILE_BLOCK;
    }

    addr = &(fpga_reg->dmac_file.checksum);
    reg_data = IO_READ_32(addr);
    if (reg_data != 0) {
        reg_clear |= REGCLR_STATUS_CHECKSUM;
    }

    if (sizeof(reg_clear) == len && put_user(reg_clear, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : put_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    mutex_unlock(&ctrl_data->file_dmac_mutex);
    return 0;
}

static long sony_fpga_set_video_transfer(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 setting = 0;
    void __iomem* addr;
    struct ref_mgr_reg __iomem* reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if ((FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_GIF || CBUSNO(ctrl_data) != module_param_gif1_busno) && FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_GIF2) {
        return 0;
    }

    if (sizeof(u32) == len && get_user(setting, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    reg = (struct ref_mgr_reg __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + REF_MGR_START_OFFSET);
    addr = &(reg->misc_ctrl);
    IO_WRITE_32(setting, addr);

    return 0;
}

// pauseとrestartはかならずPairで使うこと。Pause期間中はvideo_dmacの操作は待たさせる。
static long sony_fpga_pause_video_dmac(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    int  rc;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    rc = mutex_lock_interruptible(&ctrl_data->video_dmac_mutex);
    if (rc != 0) {
        pr_debug("pause_dmac");
        return rc;
    }
    if (ctrl_data->video_dmac_control == 1) {
        fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
        addr = &(fpga_reg->video_dmac_control);
        IO_WRITE_32(SONY_FPGA_DISABLE, addr);
    }
    ctrl_data->video_dmac_pause = SONY_FPGA_ENABLE;

    msleep(video_dmac_stop_wait);

    wmb();
    return 0;
}

static long sony_fpga_restart_video_dmac(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    struct sony_fpga_register __iomem* fpga_reg;
    u32 i = 0;
    ktime_t wait;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (ctrl_data->video_dmac_pause == SONY_FPGA_ENABLE) {
        if (ctrl_data->video_dmac_control == 1) {
            u32 memory_status;
            fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
            addr = &(fpga_reg->memory_status);

            wait = ktime_set(1, 0);
            for (i = 0; i < hbm_reset_wait_cnt; i++) {
                memory_status = IO_READ_32(addr);
                if ((memory_status & 1) == 1) {
                    break;
                }
                set_current_state(TASK_INTERRUPTIBLE);
                schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
            }
            if (i == hbm_reset_wait_cnt) {
                printk(KERN_ERR "hbm2 reset release Timeout\n");
            } else {
                pr_debug("memory_status reset release %d\n", i);
            }
            addr = &(fpga_reg->video_dmac_control);
            IO_WRITE_32(SONY_FPGA_ENABLE, addr);
        }

        wmb();
        ctrl_data->video_dmac_pause = SONY_FPGA_DISABLE;
        mutex_unlock(&ctrl_data->video_dmac_mutex);
    } else {
        printk(KERN_ERR "sony_fpga_pause is not executed");
        return -EFAULT;
    }
    return 0;
}

static long sony_fpga_rtp_aurora_reset(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM) {
        struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_RTP_AURORA_OFFSET);
        addr = &(reg->aurora_reset);
        IO_WRITE_32(3, addr);
        wmb();
        usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
        IO_WRITE_32(0, addr);
        wmb();

        return 0;
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_NEM) {
        uint i = 0;
        u32 offsets[] = { NEM_RTP_AURORA_A_OFFSET, NEM_RTP_AURORA_B_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_reset);
            IO_WRITE_32(3, addr);
            wmb();
            usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
            IO_WRITE_32(0, addr);
            wmb();

        }
        return 0;
    } else {
        printk(KERN_ERR "unsupported ioctl");
        return -EFAULT;
    }
}

static long sony_fpga_rtp_aurora_check(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    u32 status = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_NEM) {
        uint i = 0;
        u32 offsets[] = { NEM_RTP_AURORA_A_OFFSET, NEM_RTP_AURORA_B_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_status_0);
            status = IO_READ_32(addr);
            if (status == 0x37373737) {
            } else {
                return -EIO;
            }
        }
        return 0;
    }
    return 0;
}

static long sony_fpga_aurora_nstop_release(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM) {
        struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_RTP_AURORA_OFFSET);
        addr = &(reg->aurora_nStop);
        IO_WRITE_32(0x3333, addr);
        wmb();
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_NEM) {
        uint i = 0;
        u32 offsets[] = { NEM_RTP_AURORA_A_OFFSET, NEM_RTP_AURORA_B_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_nStop);
            IO_WRITE_32(0x3333, addr);
            wmb();
        }
    }
    return 0;
}
static long sony_fpga_c2c_aurora_reset(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    u32 i;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM) {
        u32 offsets[] = { BIM_C2C_AURORA_OFFSET, BIM_BIM_AURORA_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_reset);
            IO_WRITE_32(3, addr);
            wmb();
            usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
            IO_WRITE_32(0, addr);
            wmb();
        }
        return 0;
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_NEM) {
        uint i = 0;
        u32 offsets[] = { NEM_C2C_AURORA_A_OFFSET, NEM_C2C_AURORA_B_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_duplex __iomem* reg = (struct aurora_duplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_reset);
            IO_WRITE_32(3, addr);
            wmb();
            usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
            IO_WRITE_32(0, addr);
            wmb();

        }
        return 0;
    } else {
        printk(KERN_ERR "unsupported ioctl");
        return -EFAULT;
    }
}
static long sony_fpga_swr_aurora_reset(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM) {
        struct aurora_simplex __iomem* reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_SWR_AURORA_OFFSET);
        addr = &(reg->aurora_reset);
        IO_WRITE_32(3, addr);
        wmb();
        usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
        IO_WRITE_32(0, addr);
        wmb();

        return 0;
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_SIS) {
        uint i = 0;
        u32 offsets[] = { BIM_SWR_AURORA_OFFSET, BIM_BIM_AURORA_OFFSET };
        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct aurora_simplex __iomem* reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_reset);
            IO_WRITE_32(3, addr);
            wmb();
            usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
            IO_WRITE_32(0, addr);
            wmb();
        }

        return 0;
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        struct xpt_aurora_mb __iomem* reg = (struct xpt_aurora_mb __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + XPT_SWR_AURORA_OFFSET);
        addr = &(reg->aurora_reset);
        IO_WRITE_32(0xf, addr);
        wmb();
        usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
        IO_WRITE_32(0, addr);
        wmb();

        return 0;

    } else {
        printk(KERN_ERR "unsupported ioctl");
        return -EFAULT;
    }
}
static long sony_fpga_xpt_aurora_reset(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM || FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_SIS) {
        struct aurora_simplex __iomem* reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_XPT_AURORA_OFFSET);
        addr = &(reg->aurora_reset);
        IO_WRITE_32(3, addr);
        wmb();
        usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
        IO_WRITE_32(0, addr);
        wmb();

        return 0;
    } else {
        printk(KERN_ERR "unsupported ioctl");
        return -EFAULT;
    }
}

static long sony_fpga_bim_aurora_reset(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        u32 i = 0;

        u32 offsets[] = { XPT_AURORA_0_OFFSET, XPT_AURORA_1_OFFSET, XPT_AURORA_2_OFFSET, XPT_AURORA_3_OFFSET,
                          XPT_AURORA_4_OFFSET, XPT_AURORA_5_OFFSET, XPT_AURORA_6_OFFSET, XPT_AURORA_7_OFFSET };

        for (i = 0; i < sizeof(offsets) / sizeof(u32); i++) {
            struct xpt_aurora_qsfp __iomem* reg = (struct xpt_aurora_qsfp __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->aurora_reset);
            IO_WRITE_32(7, addr);
            wmb();
            usleep_range(module_param_aurora_reset_wait, module_param_aurora_reset_wait + 10);
            IO_WRITE_32(0, addr);
            wmb();
        }
        return true;
    } else {
        printk(KERN_ERR "unsupported ioctl");
        return -EFAULT;
    }
}

static long sony_fpga_get_aurora_status(struct sony_fpga_ctrl* ctrl_data, struct aurora_status_param __user* ptr, u32 len)
{
    void __iomem* addr;
    struct aurora_status_param param = {0};

    const u32 xpt_aurora_status_offsets[] = {
        XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
        XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_0),
        XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_1),
        XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_status_2),
    };

    const u32 sis_aurora_status_offsets[] = {
        SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_status_1), // QSFP#0 (ME)   aurora_status_1
        SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_status_0), // QSFP#1 (SISA) aurora_status_0
        SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_status_0), // QSFP#2 (SISB) aurora_status_0
    };

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }


    if (sizeof(param) == len && copy_from_user(&param, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        if (param.qsfp_number > QSFP24) {
            dev_err(CDEVICE(ctrl_data), "%s : invalid qsfp_number\n", __FUNCTION__);
            return -EINVAL;
        }
        addr = (ctrl_data->bar[FPGA_CTRL_BAR] + xpt_aurora_status_offsets[param.qsfp_number]);
        break;
    case FPGA_KIND_SIS:
        if (param.qsfp_number > QSFP3) {
            dev_err(CDEVICE(ctrl_data), "%s : invalid qsfp_number\n", __FUNCTION__);
            return -EINVAL;
        }
        addr = (ctrl_data->bar[FPGA_CTRL_BAR] + sis_aurora_status_offsets[param.qsfp_number]);
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    param.status = IO_READ_32(addr);

    if (copy_to_user(ptr, &param, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    return 0;
}

static long sony_fpga_i2c_tx(struct sony_fpga_ctrl* ctrl_data, struct i2c_param __user* ptr, u32 len)
{
    struct i2c_param param;
    int ret;
    int (*i2c_function)(struct sony_fpga_ctrl*, struct i2c_param*, bool);
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        i2c_function = sony_fpga_xpt_i2c_request;
        break;
    case FPGA_KIND_SIS:
        i2c_function = sony_fpga_sis_i2c_request;
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    memset(&param, 0x00, sizeof(param));
    if (sizeof(param) == len && copy_from_user(&param, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (param.size > 256 || param.i2c_bus > I2C_RTM) {
        return -EINVAL;
    }

    ret = (*i2c_function)(ctrl_data, &param, false);
    if (ret == 0) {
        if (copy_to_user(ptr, &param, len)) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
    } else {
        return ret;
    }
    return 0;
}

static long sony_fpga_i2c_rx(struct sony_fpga_ctrl* ctrl_data, struct i2c_param __user* ptr, u32 len)
{
    struct i2c_param param;
    int ret;
    int (*i2c_function)(struct sony_fpga_ctrl*, struct i2c_param*, bool);

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        i2c_function = sony_fpga_xpt_i2c_request;
        break;
    case FPGA_KIND_SIS:
        i2c_function = sony_fpga_sis_i2c_request;
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    memset(&param, 0x00, sizeof(param));

    if (sizeof(param) == len && copy_from_user(&param, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (param.size > 256 || param.i2c_bus > I2C_RTM) {
        return -EINVAL;
    }

    ret = (*i2c_function)(ctrl_data, &param, true);
    if (ret == 0) {
        if (copy_to_user(ptr, &param, len)) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
    } else {
        return ret;
    }
    return 0;
}

static long sony_fpga_get_qsfp_status(struct sony_fpga_ctrl* ctrl_data, struct qsfp_status_param __user* ptr, u32 len)
{
    int ret = 0;
    struct qsfp_status_param param;

    u32 ctrl_index = 0;
    u32 io_index = 0;

    if (ctrl_data == NULL) {
        pr_err("%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    param.qsfp_number = 0;

    if (sizeof(param) == len && copy_from_user(&param, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
        return -EINVAL;
    }

    if (param.qsfp_number > QSFP24) {
        dev_err(CDEVICE(ctrl_data), "%s : invalid qsfp_number\n", __FUNCTION__);
        return -EINVAL;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        ctrl_index = qsfp_number_resolve[param.qsfp_number][0];
        io_index = qsfp_number_resolve[param.qsfp_number][1];
        break;
    case FPGA_KIND_SIS:
        ctrl_index = 0;
        io_index = param.qsfp_number;
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    if (ctrl_index > ctrl_data->i2c_index) {
        dev_err(CDEVICE(ctrl_data), "index out of range\n");
        return -EINVAL;
    }

    if (io_index > 7) {
        dev_err(CDEVICE(ctrl_data), "index out of range\n");
        return -EINVAL;
    }

    ret = mutex_lock_interruptible(&(ctrl_data->qsfp_ctrl[ctrl_index].qsfp_mutex));
    if (ret < 0) {
        dev_err(CDEVICE(ctrl_data), "mutex_lock error\n");
        return ret;
    }

    memcpy(&param.status, &(ctrl_data->qsfp_ctrl[ctrl_index].status[io_index]), sizeof(param.status));

    mutex_unlock(&(ctrl_data->qsfp_ctrl[ctrl_index].qsfp_mutex));

    if (copy_to_user(ptr, &param, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    return 0;
}

static long sony_fpga_set_box_mode(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 box_mode = 0;
    void __iomem* addr;
    struct sony_fpga_register __iomem* reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_BIM &&
        FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_SIS) {
        return 0;
    }

    if (sizeof(u32) == len && get_user(box_mode, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    reg = (struct sony_fpga_register __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR]);
    addr = &(reg->box_mode);
    IO_WRITE_32(box_mode, addr);

    return 0;
}

static long sony_fpga_qsfp_led(struct sony_fpga_ctrl* ctrl_data, struct qsfp_led_param __user* ptr, u32 len)
{
    struct qsfp_led_param param;
    u32 i;
    int rc;
    u32 buffer_size;
    struct qsfp_led_setting* buff;

    param.num = 0;
    param.settings = NULL;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (sizeof(struct qsfp_led_param) == len && copy_from_user(&param, ptr, len)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        if (param.num > 24) {
            dev_err(CDEVICE(ctrl_data), "%s : led num range_error %d\n", __FUNCTION__, param.num);
            return -EINVAL;
        }
        break;
    case FPGA_KIND_SIS:
        if (param.num > 3) {
            dev_err(CDEVICE(ctrl_data), "%s : led num range_error %d\n", __FUNCTION__, param.num);
            return -EINVAL;
        }
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    buffer_size = sizeof(struct qsfp_led_setting) * param.num;
    buff = (struct qsfp_led_setting*)kmalloc(buffer_size, GFP_KERNEL);
    if (buff == NULL || copy_from_user(buff, (void __user*)param.settings, buffer_size)) {
        dev_err(CDEVICE(ctrl_data), "%s param.settings is NULL\n", __FUNCTION__);
        kfree(buff);
        return -EFAULT;
    }

    for (i = 0; i < param.num; i++) {
        u32 ctrl_index;
        u32 qsfp_index;

        if (buff[i].qsfp_number > QSFP24) {
            dev_err(CDEVICE(ctrl_data), "index out of range\n");
            kfree(buff);
            return -EINVAL;
        }

        switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
        case FPGA_KIND_XPT:
            ctrl_index = qsfp_number_resolve[buff[i].qsfp_number][0];
            qsfp_index = qsfp_number_resolve[buff[i].qsfp_number][1];
            if (ctrl_index > I2C_QSFP_CTRL_3 || qsfp_index > 7) {
                pr_err("index out of range\n");
                kfree(buff);
                return -EFAULT;
            }
            break;
        case FPGA_KIND_SIS:
            ctrl_index = 0;
            qsfp_index = buff[i].qsfp_number;
            if (ctrl_index > 0 || qsfp_index > 2) {
                pr_err("index out of range\n");
                kfree(buff);
                return -EFAULT;
            }
            break;
        default:
            break;
        }

        rc = mutex_lock_interruptible(&ctrl_data->qsfp_ctrl[ctrl_index].qsfp_mutex);
        if (rc != 0) {
            pr_err("mutex lock error\n");
            kfree(buff);
            return -EFAULT;
        }

        pr_debug("qsfp_num [%d](%d) led_status %d \n", buff[i].qsfp_number, qsfp_index, buff[i].led_status);
        ctrl_data->qsfp_ctrl[ctrl_index].led_status[qsfp_index] = buff[i].led_status;
        mutex_unlock(&ctrl_data->qsfp_ctrl[ctrl_index].qsfp_mutex);

    }
    kfree(buff);

    return 0;
}
static long sony_fpga_qsfp_aurora_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{

    u32 qsfp_number = 0;
    void __iomem* addr;
    u32 reset_reg;
    const u32 xpt_aurora_reset_offsets[][2] = {
        { XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_0_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_1_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_2_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_3_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_4_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_5_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_6_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 },
        { XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 0 },
        { XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 1 },
        { XPT_AURORA_7_OFFSET + offsetof(struct xpt_aurora_qsfp, aurora_reset), 2 }
    };

    // SISのAurora Reset仕様が不明で確認中
    const u32 sis_aurora_reset_offsets[][2] = {
        { SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_reset), 1 }, // QSFP#0 (Local ME)
        { SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_reset), 0 }, // QSFP#1 (Local SISA)
        { SIS_AURORA_OFFSET + offsetof(struct aurora_simplex, aurora_reset), 0 }  // QSFP#2 (Local SISB)
    };

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    if (sizeof(u32) == len && get_user(qsfp_number, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        addr = ctrl_data->bar[FPGA_CTRL_BAR] + xpt_aurora_reset_offsets[qsfp_number][0];
        // 単一コンテキストでのみ発行する仕様のため排他制御は割愛する
        reset_reg = 1 << xpt_aurora_reset_offsets[qsfp_number][1];
        break;
    case FPGA_KIND_SIS:
        addr = ctrl_data->bar[FPGA_CTRL_BAR] + sis_aurora_reset_offsets[qsfp_number][0];
        // 単一コンテキストでのみ発行する仕様のため排他制御は割愛する
        reset_reg = 1 << sis_aurora_reset_offsets[qsfp_number][1];
        break;
    default:
        return 0;
    }

    IO_WRITE_32(reset_reg, addr);
    wmb();

    reset_reg = 0;
    IO_WRITE_32(reset_reg, addr);
    return 0;
}

static long sony_fpga_qsfp_initialize(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 qsfp_number = 0;
    int ret;

    int (*setting_function)(struct sony_fpga_ctrl*, u32);
    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        setting_function = sony_fpga_xpt_qsfp_setting;
        break;
    case FPGA_KIND_SIS:
        setting_function = sony_fpga_sis_qsfp_setting;
        break;
    default:
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    if (sizeof(u32) == len && get_user(qsfp_number, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    ret = (*setting_function)(ctrl_data, qsfp_number);
    return ret;
}

static long sony_fpga_qsfp_cdr_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 qsfp_number = 0;
    int ret;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) != FPGA_KIND_XPT) {
        dev_err(CDEVICE(ctrl_data), "uniq_id is %d\n", ctrl_data->fpga_uniq_id);
        return 0;
    }

    if (sizeof(u32) == len && get_user(qsfp_number, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }

    ret = sony_fpga_xpt_qsfp_cdr_reset(ctrl_data, qsfp_number);
    return ret;
}

static long sony_fpga_qsfp_thread(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len, bool pause)
{
    u32 control = 0;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }
    if (sizeof(u32) == len && get_user(control, ptr)) {
        dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
        return -EFAULT;
    }
    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_XPT:
        if (pause) {
            return sony_fpga_pause_qsfp_thread(ctrl_data, control);
        } else {
            return sony_fpga_resume_qsfp_thread(ctrl_data, control);
        }
        break;
    case FPGA_KIND_SIS:
        if (control != 0) {
            dev_err(CDEVICE(ctrl_data), "%s : invalid parameter\n", __FUNCTION__);
        }
        if (pause) {
            return sony_fpga_pause_sis_qsfp_thread(ctrl_data);
        } else {
            return sony_fpga_resume_sis_qsfp_thread(ctrl_data);
        }
        break;
    default:
        return 0;
    }
}

static long sony_fpga_enter_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 uniq_id;
    void __iomem* addr;
    u32 physical_id = 0;
    struct aurora_simplex __iomem* aurora_reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    if (uniq_id == FPGA_KIND_XPT) {
        u32 i;
        const u32 offsets[] = { XPT_AURORA_0_OFFSET, XPT_AURORA_1_OFFSET, XPT_AURORA_2_OFFSET, XPT_AURORA_3_OFFSET,
                                XPT_AURORA_4_OFFSET, XPT_AURORA_5_OFFSET, XPT_AURORA_6_OFFSET, XPT_AURORA_7_OFFSET };

        for (i = 0; i < 8; i++) {
            struct xpt_aurora_qsfp __iomem* reg = (struct xpt_aurora_qsfp __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            // Phisical IDは固定パターンを設定
            addr = &(reg->physical_box_id);
            physical_id = ((i >> 1) + 1);

            IO_WRITE_32(physical_id, addr);
            pr_debug("offset:%x phycical %d\n", offsets[i], physical_id);
            addr = &(reg->test_result_clear);
            IO_WRITE_32(0xf, addr);

            addr = &(reg->test_sel);
            IO_WRITE_32(1, addr);
            wmb();
        }
        return 0;
    } else {
        switch (uniq_id) {
        case FPGA_KIND_BIM:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_XPT_AURORA_OFFSET);
            break;
        case FPGA_KIND_SIS:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + SIS_AURORA_OFFSET);
            break;
        default:
            pr_info("fpga_type %x invalid", uniq_id);
            return 0;
        }

        // Phisical IDを設定
        if (sizeof(u32) == len && get_user(physical_id, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        addr = &(aurora_reg->physical_box_id);
        IO_WRITE_32(physical_id, addr);
        pr_debug("busno:%x phicical %d\n", CBUSNO(ctrl_data), physical_id);

        addr = &(aurora_reg->test_result_clear);
        IO_WRITE_32(0xf, addr);

        addr = &(aurora_reg->test_sel);
        IO_WRITE_32(1, addr);
        wmb();

        return 0;
    }
}

static long sony_fpga_start_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data, struct aurora_test_result __user* ptr, u32 len)
{
    ktime_t wait;
    u32 test_enable = 1;
    void __iomem* addr;
    u32 uniq_id;
    struct aurora_simplex __iomem* aurora_reg;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    if (uniq_id == FPGA_KIND_XPT) {
        u32 i;
        const u32 offsets[] = { XPT_AURORA_0_OFFSET, XPT_AURORA_1_OFFSET, XPT_AURORA_2_OFFSET, XPT_AURORA_3_OFFSET,
                                XPT_AURORA_4_OFFSET, XPT_AURORA_5_OFFSET, XPT_AURORA_6_OFFSET, XPT_AURORA_7_OFFSET };

        for (i = 0; i < 8; i++) {
            struct xpt_aurora_qsfp __iomem* reg = (struct xpt_aurora_qsfp __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->test_enable);
            IO_WRITE_32(test_enable, addr);
            pr_debug("offset:%x %d\n", offsets[i], test_enable);
            wmb();
        }
    } else {
        switch (uniq_id) {
        case FPGA_KIND_BIM:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_XPT_AURORA_OFFSET);
            break;
        case FPGA_KIND_SIS:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + SIS_AURORA_OFFSET);
            break;
        default:
            pr_info("fpga_type %x invalid", uniq_id);
            return 0;
        }
        addr = &(aurora_reg->test_enable);
        IO_WRITE_32(test_enable, addr);
        pr_debug("%02x test_enable:%d\n", CBUSNO(ctrl_data), test_enable);
        wmb();
    }
    wait = ktime_set(module_param_aurora_test_wait, 0);
    set_current_state(TASK_INTERRUPTIBLE);
    schedule_hrtimeout(&wait, HRTIMER_MODE_REL);

    if (uniq_id == FPGA_KIND_XPT) {
        u32 i;
        const u32 offsets[] = { XPT_AURORA_0_OFFSET, XPT_AURORA_1_OFFSET, XPT_AURORA_2_OFFSET, XPT_AURORA_3_OFFSET,
                                XPT_AURORA_4_OFFSET, XPT_AURORA_5_OFFSET, XPT_AURORA_6_OFFSET, XPT_AURORA_7_OFFSET };
        struct aurora_test_result_param param;
        struct aurora_test_result result[8];

        param.num = 0;
        if (sizeof(struct aurora_test_result_param) == len && copy_from_user(&param, ptr, len)) {
            dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        if (param.num > 8) {
            dev_err(CDEVICE(ctrl_data), "%s : number error\n", __FUNCTION__);
            return -EINVAL;
        }
        for (i = 0; i < param.num; i++) {
            struct xpt_aurora_qsfp __iomem* reg = (struct xpt_aurora_qsfp __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            result[i].connect_result_pri_0 = IO_READ_32(&(reg->connect_result_pri_0));
            result[i].connect_result_pri_1 = IO_READ_32(&(reg->connect_result_pri_1));
            result[i].connect_result_meo   = IO_READ_32(&(reg->connect_result_meo));
            result[i].compare_result_pri_0 = IO_READ_32(&(reg->compare_result_pri_0));
            result[i].compare_result_pri_1 = IO_READ_32(&(reg->compare_result_pri_1));
            result[i].compare_result_meo   = IO_READ_32(&(reg->compare_result_meo));
            pr_debug("connect[%d] pri0 %x pri1 %x meo %x \n", i, result[i].connect_result_pri_0, result[i].connect_result_pri_1, result[i].connect_result_meo);
            pr_debug("compare[%d] pri0 %x pri1 %x meo %x \n", i, result[i].compare_result_pri_0, result[i].compare_result_pri_1, result[i].compare_result_meo);
            addr = &(reg->test_enable);
            test_enable = 0;
            IO_WRITE_32(test_enable, addr);
        }

        if (copy_to_user((void __user*)param.result, result, sizeof(struct aurora_test_result) * param.num)) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
    } else if (uniq_id == FPGA_KIND_BIM || uniq_id == FPGA_KIND_SIS) {
        struct aurora_test_result_param param;
        struct aurora_test_result result;

        param.num = 0;
        if (sizeof(struct aurora_test_result_param) == len && copy_from_user(&param, ptr, len)) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_from_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        if (param.num != 1) {
            dev_err(CDEVICE(ctrl_data), "%s : number error\n", __FUNCTION__);
            return -EINVAL;
        }
        result.connect_result_pri_0 = IO_READ_32(&(aurora_reg->connect_result_pri_0));
        result.connect_result_meo   = IO_READ_32(&(aurora_reg->connect_result_meo));
        result.compare_result_pri_0 = IO_READ_32(&(aurora_reg->compare_result_pri_0));
        result.compare_result_meo   = IO_READ_32(&(aurora_reg->compare_result_meo));
        result.connect_result_pri_1 = 0;
        result.compare_result_pri_1 = 0;
        pr_debug("connect pri0 %x meo %x \n", result.connect_result_pri_0, result.connect_result_meo);
        pr_debug("compare pri0 %x meo %x \n", result.compare_result_pri_0, result.compare_result_meo);
        test_enable = 0;
        addr = &(aurora_reg->test_enable);
        IO_WRITE_32(test_enable, addr);
        if (copy_to_user((void __user*)param.result, &result, sizeof(struct aurora_test_result))) {
            dev_err(CDEVICE(ctrl_data), "%s : copy_to_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
    }
    return 0;
}

static long sony_fpga_exit_aurora_test_mode(struct sony_fpga_ctrl* ctrl_data)
{
    u32 uniq_id;
    void __iomem* addr;
    struct aurora_simplex __iomem* aurora_reg;
    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    if (uniq_id == FPGA_KIND_XPT) {
        u32 i;
        const u32 offsets[] = { XPT_AURORA_0_OFFSET, XPT_AURORA_1_OFFSET, XPT_AURORA_2_OFFSET, XPT_AURORA_3_OFFSET,
                                XPT_AURORA_4_OFFSET, XPT_AURORA_5_OFFSET, XPT_AURORA_6_OFFSET, XPT_AURORA_7_OFFSET };

        for (i = 0; i < 8; i++) {
            struct xpt_aurora_qsfp __iomem* reg = (struct xpt_aurora_qsfp __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + offsets[i]);
            addr = &(reg->test_sel);
            IO_WRITE_32(0, addr);
            wmb();
        }
    } else {
        switch (uniq_id) {
        case FPGA_KIND_BIM:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + BIM_XPT_AURORA_OFFSET);
            break;
        case FPGA_KIND_SIS:
            aurora_reg = (struct aurora_simplex __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR] + SIS_AURORA_OFFSET);
            break;
        default:
            pr_info("fpga_type %x invalid", uniq_id);
            return 0;
        }
        addr = &(aurora_reg->test_sel);
        IO_WRITE_32(0, addr);
        wmb();
    }
    return 0;
}

static long sony_fpga_enable_gif_delay_adjustment(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_GIF) {
        struct sony_fpga_register __iomem* reg;
        reg = (struct sony_fpga_register __iomem*)(ctrl_data->bar[FPGA_CTRL_BAR]);
        addr = &(reg->misc_ctrl);
        IO_WRITE_32(0xffffffff, addr);
    }
    return 0;
}

static long sony_fpga_write_serial_number(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 uniq_id;
    void __iomem* addr;
    struct sony_fpga_register __iomem* reg;
    u32 serial_number = 0;
    u32 i;
    int rc;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    switch (uniq_id) {
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_BIM:
    case FPGA_KIND_SIS:
    case FPGA_KIND_XPT:
        if (sizeof(u32) == len && get_user(serial_number, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
            return -EFAULT;
        }
        pr_debug("mutex lock %02x\n", CBUSNO(ctrl_data));
        rc = mutex_lock_interruptible(&ctrl_data->nios_mutex);
        if (rc != 0) {
            pr_err("sony_fpga_set_sdi_out_ch mutex_lock error");
            return rc;
        }
        pr_debug("mutex lock %02x\n", CBUSNO(ctrl_data));
        reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
        addr = &(reg->multipurpose);
        IO_WRITE_32(serial_number, addr);
        addr = &(reg->irq_nios);
        IO_WRITE_32(0x00100000, addr);
        addr = &(reg->irq_ref_lock);
        IO_WRITE_32(1, addr);

        msleep(30);

        for (i = 0; i < 3; i++) {
            u32 data;
            int ret;
            addr = &(reg->irq_nios);
            data = IO_READ_32(addr);
            if (data == 0) {
                u32 multipurpose;
                u32 check_serial;

                ktime_t wait;

                wait = ktime_set(1, 0);
                set_current_state(TASK_INTERRUPTIBLE);
                schedule_hrtimeout(&wait, HRTIMER_MODE_REL);

                addr = &(reg->multipurpose);
                multipurpose = IO_READ_32(addr);
                if (multipurpose == 0x80000000) {
                    multipurpose = 0;
                    ret = 0;
                    IO_WRITE_32(multipurpose, addr);

                    // BoardInfo(Serial Number)確認
                    addr = (ctrl_data->bar[FPGA_CTRL_BAR] + 0x10020);
                    check_serial = IO_READ_32(addr);
                    if (check_serial != serial_number) {
                        dev_err(CDEVICE(ctrl_data), "Serial No. mismatch (w)%08x : (r)%08x\n", serial_number, check_serial);
                        ret = -EFAULT;
                    }
                    mutex_unlock(&ctrl_data->nios_mutex);
                    pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
                    return ret;
                } else {
                    printk(KERN_DEBUG "%02x multipurpose not 0x80000000\n", CBUSNO(ctrl_data));
                    mutex_unlock(&ctrl_data->nios_mutex);
                    pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
                    return -EFAULT;
                }
            } else {
                printk(KERN_DEBUG "write serial_number Warning NIOS IRQ\n");
                addr = &(reg->irq_ref_lock);
                IO_WRITE_32(1, addr);
                msleep(20);
            }
        }
        printk(KERN_DEBUG "write serial_number Error NIOS IRQ\n");
        mutex_unlock(&ctrl_data->nios_mutex);
        pr_debug("mutex unlock %02x\n", CBUSNO(ctrl_data));
        break;
    default:
        return 0;
        break;
    }
    return 0;
}

static long sony_fpga_set_axi_firewall_wait(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    u32 fpga_kind;
    u16 timeout_reg_offsets[10] = { 0x1030, 0x1034, 0x1038, 0x103c, 0x1040,
                                    0x2030, 0x2034, 0x2038, 0x203c, 0x2040 };
    u32 i;
    u32 wait = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    fpga_kind = FPGA_KIND(ctrl_data->fpga_uniq_id);

    switch (fpga_kind) {
    case FPGA_KIND_BIM:
    case FPGA_KIND_SIS:
        wait = 0x000000f0;
        break;
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_SWR:
        wait = 0x0000017c;
        break;
    default:
        return 0;
    }
    for (i = 0; i < 10; i++) {
        addr = (ctrl_data->bar[FPGA_CTRL_BAR] + timeout_reg_offsets[i]);
        IO_WRITE_32(wait, addr);
        pr_debug("type:%d addr=%x\n", fpga_kind, timeout_reg_offsets[i]);
    }
    wmb();
    return 0;
}

static const u32 SIS_FPGA_TX_SETTING[5][2] = {
    { 0x00008680, 0x10101010 },  // Local Port B/C Diffctrl 780mV
    { 0x00008684, 0x00001010 },  // Local Port A   Diffctrl 780mV
    { 0x000086A0, 0x05050505 },  // Local Port B/C Precursor  1.17dB
    { 0x000086A4, 0x00000505 },  // Local Port A   Precursor  1.17dB
    { 0x0000847c, 0x00000000 },  // Aurora DEF
};
static const u32 NEM_FPGA_DEF_OFFSET[2] = { 0x847c, 0x867c };
static const u32 BIM_FPGA_DEF_OFFSET[2] = { 0x827c, 0x847c };

static long sony_fpga_aurora_init(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    void __iomem *bar0_addr;
    u32 i = 0;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    bar0_addr = ctrl_data->bar[FPGA_CTRL_BAR];

    switch (FPGA_KIND(ctrl_data->fpga_uniq_id)) {
    case FPGA_KIND_SIS:
        ARRAY_FOREACH(SIS_FPGA_TX_SETTING, i) {
            addr = (void __iomem*)(bar0_addr + SIS_FPGA_TX_SETTING[i][0]);
            IO_WRITE_32(SIS_FPGA_TX_SETTING[i][1], addr);
        }
        wmb();
        break;
    case FPGA_KIND_NEM:
        ARRAY_FOREACH(NEM_FPGA_DEF_OFFSET, i) {
            addr = (void __iomem*)(bar0_addr + NEM_FPGA_DEF_OFFSET[i]);
            IO_WRITE_32(0, addr);
        }
        wmb();
        break;
    case FPGA_KIND_BIM:
        ARRAY_FOREACH(BIM_FPGA_DEF_OFFSET, i) {
            addr = (void __iomem*)(bar0_addr + BIM_FPGA_DEF_OFFSET[i]);
            IO_WRITE_32(0, addr);
        }
        wmb();
        break;
    case FPGA_KIND_XPT:
        addr = (void __iomem*)(bar0_addr + 0x90cc);
        IO_WRITE_32(0, addr);
        wmb();
        break;
    case FPGA_KIND_SWR:
        addr = (void __iomem*)(bar0_addr + 0x81b0);
        IO_WRITE_32(0x00010000, addr);
        wmb();
        break;
    case FPGA_KIND_GIF:
    case FPGA_KIND_GIF2:
        addr = (void __iomem*)(bar0_addr + 0x81b0);
        IO_WRITE_32(0x00000001, addr);
        wmb();
        break;
    }

    return 0;
}

static long sony_fpga_set_sdi_mute(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 uniq_id;
    void __iomem* addr;
    void __iomem *bar0_addr;
    u32 offon = 0;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    if (uniq_id == FPGA_KIND_SIS) {
        if (sizeof(u32) == len && get_user(offon, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
            return -EFAULT;
        }

        bar0_addr = ctrl_data->bar[FPGA_CTRL_BAR];
        addr = (void __iomem*)(bar0_addr + 0x81a8);
        IO_WRITE_32(offon, addr);

        wmb();
    }

    return 0;
}

static long sony_fpga_set_sdi_auto_reset(struct sony_fpga_ctrl* ctrl_data, u32 __user* ptr, u32 len)
{
    u32 uniq_id;
    void __iomem* addr;
    void __iomem *bar0_addr;
    u32 offon = 0;

    if (ctrl_data == NULL) {
        return -EFAULT;
    }

    uniq_id = FPGA_KIND(ctrl_data->fpga_uniq_id);
    if (uniq_id == FPGA_KIND_SIS) {
        if (sizeof(u32) == len && get_user(offon, ptr)) {
            dev_err(CDEVICE(ctrl_data), "%s : get_user failed\n", __FUNCTION__);
            return -EFAULT;
        }

        bar0_addr = ctrl_data->bar[FPGA_CTRL_BAR];
        addr = (void __iomem*)(bar0_addr + 0x82f4);
        IO_WRITE_32(offon, addr);

        wmb();
    }

    return 0;
}

static long sony_fpga_mib_start(struct sony_fpga_ctrl* ctrl_data)
{
    void __iomem* addr;
    void __iomem *bar5_addr;

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }

    bar5_addr = ctrl_data->bar[FPGA_BAR5];

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_BIM) {
        addr = (void __iomem*)(bar5_addr + 0x0010);
        IO_WRITE_32(1, addr);
    }

    return 0;
}

static const u32 NEM_FPGA_MIB_COUNTER_RESET[6][3] = {
    { 0x840010, 0xfffffffe, 1 },     // Adaptor, TX, Main
    { 0x840014, 0xfffffffe, 1 },     // Adaptor, TX, Sub
    { 0x840018, 0xffffffe0, 0x1f },  // Adaptor, RX, Main
    { 0x84001c, 0xffffffe0, 0x1f },  // Adaptor, RX, Sub
    { 0x840200, 0, 1 },              // CMAC, All
    { 0x840204, 0, 1 }               // Validation, ALL
};
static const u32 NEM_FPGA_RESET_BARS[2] = { FPGA_BAR1, FPGA_BAR2 };

static long sony_fpga_reset_mib_counter(struct sony_fpga_ctrl* ctrl_data)
{

    if (ctrl_data == NULL) {
        printk(KERN_ERR "%s ctrl_data is NULL\n", __FUNCTION__);
        return -EFAULT;
    }


    // Adaptor, TX, Main
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_NEM) {
        void __iomem* addr;
        void __iomem *bar_addr;
        uint i = 0;

        ARRAY_FOREACH(NEM_FPGA_MIB_COUNTER_RESET, i) {
            u32 read_value;
            u32 write_value;
            uint j = 0;
            ARRAY_FOREACH(NEM_FPGA_RESET_BARS, j) {
                bar_addr = ctrl_data->bar[NEM_FPGA_RESET_BARS[j]];
                addr = (void __iomem*)(bar_addr + NEM_FPGA_MIB_COUNTER_RESET[i][0]);
                read_value = IO_READ_32(addr);
                write_value = (read_value & NEM_FPGA_MIB_COUNTER_RESET[i][1]) | NEM_FPGA_MIB_COUNTER_RESET[i][2];
                pr_debug("addr[%d]: %llx value: %x %x\n", j, (u64)addr, read_value, write_value);
                IO_WRITE_32(write_value, addr);
                IO_WRITE_32(read_value, addr);
            }
        }
    }

    return 0;
}
MODULE_LICENSE("Dual BSD/GPL");
