#include <linux/module.h>
#include <linux/pci.h>
#include <linux/cdev.h>

#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>

#include <nv-p2p.h>
#include "./sony_fpga_cmd.h"
#include "./sony_fpga.h"
#include "./sony_fpga_sysfs.h"
#include "./sony_fpga_netif.h"
#include "./sony_fpga_xpt.h"
#include "./sony_fpga_sis.h"

#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>

MODULE_AUTHOR("ULM Project <ulm-soft@vsp.cpg.sony.co.jp>");
MODULE_DESCRIPTION("Sony PCIe FPGA Driver");
MODULE_VERSION(SONY_FPGA_DRIVER_VERSION);
MODULE_LICENSE("GPL v2");

#ifdef DEBUG
#define TRACE_ENTER() printk(KERN_DEBUG "%s : enter\n", __FUNCTION__)
#define TRACE_EXIT() printk(KERN_DEBUG "%s : exit\n", __FUNCTION__)
#else
#define TRACE_ENTER()
#define TRACE_EXIT()
#endif

#ifndef GPU_DISABLE
extern void video_dmac_start_func(struct work_struct* work);
#endif

/* for init */
static int  scan_bars(struct sony_fpga_ctrl *ctrl_data, struct pci_dev *dev) __init;
static int  map_bars(struct sony_fpga_ctrl *ctrl_data, struct pci_dev *dev) __init;
static int  alloc_ctrl_dmac_buffer(struct sony_fpga_ctrl* ctrl_data) __init;
static int  init_chrdev (struct sony_fpga_ctrl *ctr_data) __init;
static void free_ctrl_dmac_buffer(struct sony_fpga_ctrl* ctrl_data) __exit;

static void sony_fpga_probe_err(struct sony_fpga_ctrl* ctrl_data, bool cdev) __init;
static int  sony_fpga_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) __init;
static void sony_fpga_pci_remove(struct pci_dev *dev) __exit;

static int alloc_file_dmac_descriptor(struct sony_fpga_ctrl* ctrl_data, int pages);
/* from sub module */
extern struct file_operations sony_fpga_fops;
extern struct attribute_group attr_group;

/// 正誤判定用エリア
u32*       process_status_area = NULL;
static dma_addr_t process_status_bus_addr = 0;

u32*       process_status_area_xpt = NULL;
static dma_addr_t process_status_bus_addr_xpt = 0;

/* Module Parameter */
static int module_param_gpu_exist = 0;
module_param(module_param_gpu_exist, int, S_IRUGO);

static int module_param_slave_count = 0;
module_param(module_param_slave_count, int, S_IRUGO);

static ulong module_param_video_format = 0x8a24; // 4K 2SI 59.94
module_param(module_param_video_format, ulong, S_IRUGO);

static ulong module_param_vd_check = 0;
module_param(module_param_vd_check, ulong, S_IRUGO | S_IWUSR);

#ifdef SIGNAL_SELECT_4K
static int module_param_gpu_assign = 0;
module_param(module_param_gpu_assign, int, S_IRUGO);
#endif
int module_param_ctrl_dmac_debug_enable = 0;
module_param(module_param_ctrl_dmac_debug_enable, int, S_IRUGO | S_IWUSR);

ulong module_param_board_revision = 0; // 4K 2SI 59.94
module_param(module_param_board_revision, ulong, S_IRUGO);

ulong module_param_gif1_busno = 0x06;
module_param(module_param_gif1_busno, ulong, S_IRUGO);

bool module_param_xpt_vd_enable = false;
module_param(module_param_xpt_vd_enable, bool, S_IRUGO);

static uint ignore_busnos[16] = {0};
static uint ignore_busno_num;

u32 vd_counter = 0;
vd_wait_queue_t vd_wait_queue;
u32 int_counter = 0;

module_param_array(ignore_busnos, uint, &ignore_busno_num, S_IRUGO | S_IWUSR);
/* TODO 値は未定 */
/* ctrl_dmac read dmaバッファのページサイズ定義 */
static const u32 CTRL_DMAC_PAGES[FPGA_KIND_MAX][FPGA_MODE_MAX] = {
    {65, 65, 0}, // GIF 256K + descriptor
    {65, 65, 0}, // SWR 256K + descriptor
    {65, 65, 17}, // SDI 256K + descriptor
    {17, 17, 0}, // NEM 64K + descriptor
    {23, 23, 23}, // BIM 81K + descriptor
    {17, 17, 0}, // XPT 64K + descriptor
    {65, 65, 0}, // GIF2 256K + descriptor
    {23, 23, 23}, // SIS 81K + descriptor
};

static const u32 FILE_DMAC_PAGES[FPGA_KIND_MAX] = {
    5805, // GIF (4Kx20bit / 4096)
    16,   // SWR (64KB /4096)
    16,   // SDI (64KB / 4096)
    0,    // NEM
    24,   // BIM (64KB / 4096) 4K PureP Audio で 96KB
    16,   // XPT (64KB / 4096)
    16,   // GIF2 (64KB / 4096)
    24,   // SIS (64KB / 4096) (slave fileが64KB Audioは48KB固定)
          //                   4K PureP Audio で 96KB
};

const u32 CTRL_DMAC_WRITE_SIZE[CTRL_DMAC_WRITE_MAX][FPGA_KIND_MAX] = {
    // Normal
    {
        0x06, // GIF
        0x28, // SWR
        0x4c, // SDI
        0x00, // NEM
        0x64, // BIM
        0x06, // XPT
        0x06, // GIF2
        0x44, // SIS
    },
    // Compare Test
    {
        0x08, // GIF
        0x08, // SWR
        0x08, // SDI
        0x00, // NEM
        0x08, // BIM
        0x08, // XPT
        0x08, // GIF2
        0x08, // SIS
    },
    // Intel基板向けの暫定
    {
        0x08, // GIF
        0x08, // SWR
        0x08, // SDI
        0x00, // NEM
        0x00, // BIM
        0x00, // XPT
        0x00, // GIF2
        0x00, // SIS
    },
};

/**** Implementation ****/

static int __init init_chrdev(struct sony_fpga_ctrl* ctrl_data)
{
    int dev_major = 0;
    int dev_minor = 0;
    int devno     = -1;
    char cdev_name[64] = {0};
    int result;

    snprintf(cdev_name, 64, SONY_FPGA_DEVFILE "_%02x", CBUSNO(ctrl_data));
    result    = alloc_chrdev_region(&(ctrl_data->cdevno), dev_minor, 1, cdev_name);

    dev_major     = MAJOR(ctrl_data->cdevno);

    if (result < 0) {
        printk(KERN_ERR "cannot get major ID %d", dev_major);
    }

    devno         = MKDEV(dev_major, dev_minor);

    cdev_init(&(ctrl_data->cdev), &sony_fpga_fops);
    ctrl_data->cdev.owner = THIS_MODULE;
    ctrl_data->cdev.ops   = &sony_fpga_fops;
    result = cdev_add(&(ctrl_data->cdev), devno, 1);

    if (result) {
        return -1;
    }
    return 0;
}

static int __init scan_bars(struct sony_fpga_ctrl* ctrl_data, struct pci_dev* dev)
{
    int i;
    for (i = 0; i < NUM_BARS; i++) {
        ctrl_data->bar_size[i]  = pci_resource_len(dev, i);
#ifdef DEBUG
        unsigned long bar_start = pci_resource_start(dev, i);
        unsigned long bar_end   = pci_resource_end(dev, i);
        unsigned long bar_flags = pci_resource_flags(dev, i);
        dev_info(&dev->dev, "BAR[%d] 0x%08lx-0x%08lx flags 0x%08lx, length %d", i, bar_start, bar_end, bar_flags, (int)ctrl_data->bar_size[i]);
#endif
    }
    return 0;
}

static int __init map_bars(struct sony_fpga_ctrl* ctrl_data, struct pci_dev* dev)
{
    int i;
    for (i = 0; i < NUM_BARS; i++) {
        ctrl_data->bar[i] = pci_ioremap_bar(dev, i);
        if (ctrl_data->bar[i]) {
            dev_info(&dev->dev, "BAR[%d] mapped to 0x%p, length %lu", i, ctrl_data->bar[i], (long unsigned int)ctrl_data->bar_size[i]);
        }
    }
    return 0;
}

static void unmap_bars(struct sony_fpga_ctrl* ctrl_data, struct pci_dev* dev)
{
    int i;
    for (i = 0; i < NUM_BARS; i++) {
        if (ctrl_data->bar[i]) {
            pci_iounmap(dev, ctrl_data->bar[i]);
            ctrl_data->bar[i] = NULL;
        }
    }
}

static void free_ctrl_dmac_buffer(struct sony_fpga_ctrl* ctrl_data)
{
    if (ctrl_data->ctrl_dmac_buffer != NULL) {
        pci_free_consistent(ctrl_data->pci_dev, ctrl_data->ctrl_dmac_pages * PAGE_SIZE,
                ctrl_data->ctrl_dmac_buffer, ctrl_data->ctrl_dmac_buffer_bus_addr);
        ctrl_data->ctrl_dmac_buffer = NULL;
        ctrl_data->ctrl_dmac_buffer_bus_addr = 0;
    }
}

static int __init alloc_file_dmac_descriptor(struct sony_fpga_ctrl* ctrl_data, int pages)
{
    u32 page_num = 0;
    TRACE_ENTER();

    page_num = pages * sizeof(u64);
    ctrl_data->file_dmac_descriptor_virt_buffer = pci_alloc_consistent(ctrl_data->pci_dev, page_num,
                                                     &(ctrl_data->file_dmac_descriptor_bus_addr));
    if (!ctrl_data->file_dmac_descriptor_virt_buffer) {
        return -ENOMEM;
    }
    ctrl_data->file_dmac_descriptor_size = page_num;
    dev_info(&ctrl_data->pci_dev->dev, "File DMAC Descriptor: virt 0x%016llx bus 0x%016llx size %d 0x%016llx\n",
                       (u64)ctrl_data->file_dmac_descriptor_virt_buffer,
                       (u64)ctrl_data->file_dmac_descriptor_bus_addr,
                       ctrl_data->file_dmac_descriptor_size, virt_to_phys(ctrl_data->file_dmac_descriptor_virt_buffer));
    TRACE_EXIT();

    return 0;
}
static int __init alloc_video_descriptor(struct sony_fpga_ctrl* ctrl_data, int pages)
{
    TRACE_ENTER();
    ctrl_data->video_descriptor_virt_addr = pci_alloc_consistent(ctrl_data->pci_dev, PAGE_SIZE*pages,
                                                     &(ctrl_data->video_descriptor_bus_addr));
    if (!ctrl_data->video_descriptor_virt_addr) {
        ctrl_data->video_descriptor_pages = 0;
        return -ENOMEM;
    }

    ctrl_data->video_descriptor_pages = pages;
    dev_info(&ctrl_data->pci_dev->dev, "Video Descriptor: virt 0x%016llx bus 0x%016llx pages %d\n",
                       (u64)ctrl_data->video_descriptor_virt_addr,
                       (u64)ctrl_data->video_descriptor_bus_addr,
                       ctrl_data->video_descriptor_pages);

    memset(ctrl_data->video_descriptor_virt_addr, 0, PAGE_SIZE * pages);

    ctrl_data->free_video_descriptor = (struct dma_desc*)ctrl_data->video_descriptor_virt_addr;
    ctrl_data->free_video_descriptor_cnt = pages * PAGE_SIZE / sizeof(struct dma_desc);

    TRACE_EXIT();

    return 0;
}
static int __init alloc_ctrl_dmac_buffer(struct sony_fpga_ctrl* ctrl_data)
{
    TRACE_ENTER();
    ctrl_data->ctrl_dmac_buffer = pci_alloc_consistent(ctrl_data->pci_dev, PAGE_SIZE * ctrl_data->ctrl_dmac_pages,
                                                     &(ctrl_data->ctrl_dmac_buffer_bus_addr));
    if (!ctrl_data->ctrl_dmac_buffer) {
        return -ENOMEM;
    }

    dev_info(CDEVICE(ctrl_data), "Ctrl DMAC Buffer: virt 0x%016llx bus 0x%016llx pages %d\n",
                       (u64)ctrl_data->ctrl_dmac_buffer,
                       (u64)ctrl_data->ctrl_dmac_buffer_bus_addr,
                       ctrl_data->ctrl_dmac_pages);

    memset(ctrl_data->ctrl_dmac_buffer, 0, PAGE_SIZE * ctrl_data->ctrl_dmac_pages);
    TRACE_EXIT();

    return 0;
}

#ifdef DEBUG
static ssize_t stb_ctrl_status_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret;
    struct sony_fpga_ctrl* ctrl = (struct sony_fpga_ctrl*)file_inode(f)->i_private;
    ret = simple_read_from_buffer(buf, len, ppos, ctrl, sizeof(struct sony_fpga_ctrl));
    return ret;
}
static ssize_t stb_ctrl_status_write(struct file* f, const char __user* buf, size_t len, loff_t* ppos)
{
    return 0;
}

char* ctrl_status_map_buff = NULL;
u32 ctrl_status_len = 0;
#define LOCATION_PRINT(len, buff, member,size)        { \
    u32 llen = 0; \
    llen = scnprintf(buff, 4096, #member " : ofs:%lx(%ld) sz:%ld\n", offsetof(struct sony_fpga_ctrl, member), offsetof(struct sony_fpga_ctrl, member), size); \
    buff += llen; \
    len += llen; \
}


static ssize_t ctrl_status_map_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret;
    struct sony_fpga_ctrl* ctrl = (struct sony_fpga_ctrl*)file_inode(f)->i_private;
    char* buff = NULL;

    if (*ppos == 0) {
        if (ctrl_status_map_buff == NULL) {
            ctrl_status_map_buff = vmalloc(16*PAGE_SIZE);
        }
        buff = ctrl_status_map_buff;
        ctrl_status_len = 0;
        LOCATION_PRINT(ctrl_status_len, buff, pci_dev, sizeof(ctrl->pci_dev));
        LOCATION_PRINT(ctrl_status_len, buff, bar, sizeof(ctrl->bar));
        LOCATION_PRINT(ctrl_status_len, buff, bar_size, sizeof(ctrl->bar_size));
        LOCATION_PRINT(ctrl_status_len, buff, msi_enabled, sizeof(ctrl->msi_enabled));
        LOCATION_PRINT(ctrl_status_len, buff, fpga_uniq_id, sizeof(ctrl->fpga_uniq_id));
        LOCATION_PRINT(ctrl_status_len, buff, local_id, sizeof(ctrl->local_id));
        LOCATION_PRINT(ctrl_status_len, buff, cdevno, sizeof(ctrl->cdevno));
        LOCATION_PRINT(ctrl_status_len, buff, cdev, sizeof(ctrl->cdev));
        LOCATION_PRINT(ctrl_status_len, buff, ctrl_dmac_mutex, sizeof(ctrl->ctrl_dmac_mutex));
        LOCATION_PRINT(ctrl_status_len, buff, ctrl_dmac_pages, sizeof(ctrl->ctrl_dmac_pages));
        LOCATION_PRINT(ctrl_status_len, buff, ctrl_dmac_buffer_bus_addr, sizeof(ctrl->ctrl_dmac_buffer_bus_addr));
        LOCATION_PRINT(ctrl_status_len, buff, ctrl_dmac_buffer, sizeof(ctrl->ctrl_dmac_buffer));
        LOCATION_PRINT(ctrl_status_len, buff, ctrl_dmac_wp, sizeof(ctrl->ctrl_dmac_wp));
        LOCATION_PRINT(ctrl_status_len, buff, cmp_test_ctrl, sizeof(ctrl->cmp_test_ctrl));
        LOCATION_PRINT(ctrl_status_len, buff, gd, sizeof(ctrl->gd));
        LOCATION_PRINT(ctrl_status_len, buff, video_descriptor_pages, sizeof(ctrl->video_descriptor_pages));
        LOCATION_PRINT(ctrl_status_len, buff, video_descriptor_virt_addr, sizeof(ctrl->video_descriptor_virt_addr));
        LOCATION_PRINT(ctrl_status_len, buff, video_descriptor_bus_addr, sizeof(ctrl->video_descriptor_bus_addr));
        LOCATION_PRINT(ctrl_status_len, buff, free_video_descriptor, sizeof(ctrl->free_video_descriptor));
        LOCATION_PRINT(ctrl_status_len, buff, free_video_descriptor_cnt, sizeof(ctrl->free_video_descriptor_cnt));
        LOCATION_PRINT(ctrl_status_len, buff, video_dmac_control, sizeof(ctrl->video_dmac_control));
        LOCATION_PRINT(ctrl_status_len, buff, video_dmac_pause, sizeof(ctrl->video_dmac_pause));
        LOCATION_PRINT(ctrl_status_len, buff, video_dmac_mutex, sizeof(ctrl->video_dmac_mutex));
        LOCATION_PRINT(ctrl_status_len, buff, video_desc_set, sizeof(ctrl->video_desc_set));
        LOCATION_PRINT(ctrl_status_len, buff, current_desc_set_index, sizeof(ctrl->current_desc_set_index));
        LOCATION_PRINT(ctrl_status_len, buff, user_pid, sizeof(ctrl->user_pid));
        LOCATION_PRINT(ctrl_status_len, buff, file_dmac_descriptor_bus_addr, sizeof(ctrl->file_dmac_descriptor_bus_addr));
        LOCATION_PRINT(ctrl_status_len, buff, file_dmac_descriptor_virt_buffer, sizeof(ctrl->file_dmac_descriptor_virt_buffer));
        LOCATION_PRINT(ctrl_status_len, buff, file_dmac_descriptor_size, sizeof(ctrl->file_dmac_descriptor_size));
        LOCATION_PRINT(ctrl_status_len, buff, file_dmac_mutex, sizeof(ctrl->file_dmac_mutex));
        LOCATION_PRINT(ctrl_status_len, buff, dfs_root, sizeof(ctrl->dfs_root));
        LOCATION_PRINT(ctrl_status_len, buff, nios_mutex, sizeof(ctrl->nios_mutex));
#ifdef FPGA_DUMMY
        LOCATION_PRINT(ctrl_status_len, buff, dummy_fpga_reg, sizeof(ctrl->dummy_fpga_reg));
#endif
    }
    ret = simple_read_from_buffer(buf, len, ppos, ctrl_status_map_buff, ctrl_status_len);
    if (ret == 0) {
        vfree(ctrl_status_map_buff);
        ctrl_status_map_buff = NULL;
        ctrl_status_len = 0;
    }

    return ret;
}

char* fpga_reg_map_buff = NULL;
u32 fpga_reg_map_len = 0;
#define FPGA_REG_LOCATION_PRINT(len, buff, member,size)        { \
    u32 llen = 0; \
    llen = scnprintf(buff, 4096, #member " : ofs:%lx(%ld) sz:%ld\n", offsetof(struct sony_fpga_register, member), offsetof(struct sony_fpga_register, member), size); \
    buff += llen; \
    len += llen; \
}

static ssize_t fpga_reg_map_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret;
    struct sony_fpga_ctrl* ctrl = (struct sony_fpga_ctrl*)file_inode(f)->i_private;
    char* buff = NULL;
    struct sony_fpga_register* reg = (struct sony_fpga_register*)ctrl->bar[FPGA_CTRL_BAR];

    if (*ppos == 0) {
        if (fpga_reg_map_buff == NULL) {
            fpga_reg_map_buff = vmalloc(16*PAGE_SIZE);
        }
        buff = fpga_reg_map_buff;
        fpga_reg_map_len = 0;
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, pcie_version, sizeof(reg->pcie_version));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, pcie_date, sizeof(reg->pcie_date));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, fpga_version, sizeof(reg->fpga_version));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, fpga_date, sizeof(reg->fpga_date));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, fpga_uniq_id, sizeof(reg->fpga_uniq_id));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, video_format, sizeof(reg->video_format));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, com_e_init, sizeof(reg->com_e_init));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, system_reset, sizeof(reg->system_reset));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_ref_lock, sizeof(reg->irq_ref_lock));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, gpu_exist, sizeof(reg->gpu_exist));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_enable_1, sizeof(reg->irq_enable_1));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_enable_2, sizeof(reg->irq_enable_2));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_enable_3, sizeof(reg->irq_enable_3));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, local_id, sizeof(reg->local_id));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, ctrl_dmac_control, sizeof(reg->ctrl_dmac_control));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, video_dmac_control, sizeof(reg->video_dmac_control));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, ctrl_master_mode, sizeof(reg->ctrl_master_mode));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, com_e_vd_count, sizeof(reg->com_e_vd_count));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_vd_count, sizeof(reg->irq_vd_count));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, irq_vd_control, sizeof(reg->irq_vd_control));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast_start, sizeof(reg->multi_cast_start));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, ctrl_dmac_write_adrs, sizeof(reg->ctrl_dmac_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, ctrl_dmac_read_adrs, sizeof(reg->ctrl_dmac_read_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, frame_count_adrs, sizeof(reg->frame_count_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast1_write_adrs, sizeof(reg->multi_cast1_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast2_write_adrs, sizeof(reg->multi_cast2_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast3_write_adrs, sizeof(reg->multi_cast3_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast4_write_adrs, sizeof(reg->multi_cast4_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast5_write_adrs, sizeof(reg->multi_cast5_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast6_write_adrs, sizeof(reg->multi_cast6_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast7_write_adrs, sizeof(reg->multi_cast7_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, multi_cast8_write_adrs, sizeof(reg->multi_cast8_write_adrs));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, ctrl_dmac_size, sizeof(reg->ctrl_dmac_size));
        FPGA_REG_LOCATION_PRINT(fpga_reg_map_len, buff, dmac_media, sizeof(reg->dmac_media));
    }
    ret = simple_read_from_buffer(buf, len, ppos, fpga_reg_map_buff, fpga_reg_map_len);
    if (ret == 0) {
        vfree(fpga_reg_map_buff);
        fpga_reg_map_buff = NULL;
        fpga_reg_map_len = 0;
    }
    return ret;
}

static ssize_t video_desc_set_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret = 0;
    struct sony_fpga_ctrl* ctrl = (struct sony_fpga_ctrl*)file_inode(f)->i_private;

    ret = simple_read_from_buffer(buf, len, ppos, ctrl->video_desc_set, sizeof(*ctrl->video_desc_set) * VIDEO_DESC_SET_MAX);
    return ret;
}

#ifdef FPGA_DUMMY
static ssize_t fpga_reg_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret = 0;
    struct sony_fpga_ctrl* ctrl = (struct sony_fpga_ctrl*)file_inode(f)->i_private;

    ret = simple_read_from_buffer(buf, len, ppos, ctrl->dummy_fpga_reg, sizeof(ctrl->dummy_fpga_reg));
    return ret;
}

static struct file_operations fpga_reg_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = fpga_reg_read,
    .write = NULL,
};
#endif

static ssize_t ctrl_dmac_ignore_list_read(struct file* f, char __user *buf, size_t len, loff_t *ppos)
{
    int ret = 0;
    ret = simple_read_from_buffer(buf, len, ppos, &ignore_list, sizeof(ignore_list));
    return ret;
}

static struct file_operations video_desc_set_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = video_desc_set_read,
    .write = NULL,
};
static struct file_operations ctrl_status_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = stb_ctrl_status_read,
    .write = stb_ctrl_status_write,
};

static struct file_operations ctrl_status_map_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = ctrl_status_map_read,
};

static struct file_operations fpga_reg_map_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = fpga_reg_map_read,
};

static struct file_operations ctrl_dmac_ignore_list_debugfs_fops = {
    .owner = THIS_MODULE,
    .read  = ctrl_dmac_ignore_list_read,
};

static int dfs_init(struct sony_fpga_ctrl* ctrl_data)
{
    struct dentry *dent;
    char debugfs_dir_name[256] = {0};

    snprintf(debugfs_dir_name, 256, "sony_fpga_ctrl_%02x", CBUSNO(ctrl_data));
    dent = debugfs_create_dir(debugfs_dir_name, NULL);

    ctrl_data->dfs_root = dent;

    debugfs_create_file("ctrl_status", 0444, dent, ctrl_data, &ctrl_status_debugfs_fops);
    debugfs_create_file("video_desc_set", 0444, dent, ctrl_data, &video_desc_set_debugfs_fops);
    debugfs_create_file("ctrl_status_map", 0444, dent, ctrl_data, &ctrl_status_map_debugfs_fops);
    debugfs_create_file("fpga_reg_map", 0444, dent, ctrl_data, &fpga_reg_map_debugfs_fops);
#ifdef FPGA_DUMMY
    debugfs_create_file("fpga_reg", 0444, dent, ctrl_data, &fpga_reg_debugfs_fops);
#endif
    debugfs_create_file("ctrl_dmac_ignore_list", 0444, dent, ctrl_data, &ctrl_dmac_ignore_list_debugfs_fops);

    return 0;
}
#endif

static irqreturn_t vd_isr(int irq, void *data)
{
    struct sony_fpga_ctrl* ctrl_data = (struct sony_fpga_ctrl*)data;
    struct sony_fpga_register __iomem* fpga_reg;
    void __iomem* addr;
#ifndef FPGA_DUMMY
    static int pre_counter = 0;
    static u64 prev_time = 0;
    struct timespec now;
    u64 now_time = 0;
#endif

    if (ctrl_data == NULL) {
        return IRQ_NONE;
    }

    fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
    addr = &(fpga_reg->irq_vd_count);
#ifndef FPGA_DUMMY
    int_counter = IO_READ_32(addr);
    getrawmonotonic(&now);
    now_time = now.tv_sec * 1000000000 + now.tv_nsec;
    if (module_param_vd_check > 0) {
        ulong delta = now_time - prev_time;

        if (pre_counter < int_counter) {
            if ((int_counter - pre_counter) != 1) {
                pr_debug("%d %d %u %u\n", pre_counter, int_counter, vd_counter, *((u8*)ctrl_data->ctrl_dmac_buffer + 0x1d4));
            }
        } else {
            if ((int_counter + 24 - pre_counter) != 1) {
                pr_debug("%d %d %u %u\n", pre_counter, int_counter, vd_counter, *((u8*)ctrl_data->ctrl_dmac_buffer + 0x1d4));
            }
        }
        if (delta > (4000000 + module_param_vd_check) || delta < (4000000 - module_param_vd_check)) {
            pr_debug("%llu %llu %u %u\n", prev_time / 1000 , now_time / 1000, vd_counter,*((u8*)ctrl_data->ctrl_dmac_buffer + 0x1d4));
        }
    }
    pre_counter = int_counter;
    prev_time = now_time;
#else
    int_counter++;
    if (int_counter > 23) {
        int_counter = 0;
    }
#endif

    rmb();
    if ((int_counter & 0b11) == 0) {
        vd_counter++;
    }

    vd_wake_up_interruptible(&vd_wait_queue);
    return IRQ_HANDLED;
}


static void __init sony_fpga_probe_err(struct sony_fpga_ctrl* ctrl_data, bool cdev)
{
    if (ctrl_data == NULL) {
        return;
    }

    sysfs_remove_group(&ctrl_data->pci_dev->dev.kobj, &attr_group);

    if ((FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_GIF && CBUSNO(ctrl_data) == module_param_gif1_busno) && ctrl_data->pci_dev->device != SONY_FPGA_DID_VIDEO) {
        if (ctrl_data->pci_dev->irq != 0) {
            free_irq(ctrl_data->pci_dev->irq, (void*)ctrl_data);
            pci_disable_msi(ctrl_data->pci_dev);
        } else if (process_status_area != NULL) {
            pci_free_consistent(ctrl_data->pci_dev, PROCESS_STATUS_SIZE, process_status_area, process_status_bus_addr);
            process_status_area = NULL;
            process_status_bus_addr = 0;
        }
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        if (ctrl_data->pci_dev->irq != 0) {
            free_irq(ctrl_data->pci_dev->irq, (void*)ctrl_data);
            pci_disable_msi(ctrl_data->pci_dev);
        } else if (process_status_area_xpt != NULL) {
            pci_free_consistent(ctrl_data->pci_dev, PROCESS_STATUS_SIZE, process_status_area_xpt, process_status_bus_addr_xpt);
            process_status_area_xpt = NULL;
            process_status_bus_addr_xpt = 0;
        }
    }

    if (ctrl_data->file_dmac_descriptor_virt_buffer) {
        pci_free_consistent(ctrl_data->pci_dev, ctrl_data->file_dmac_descriptor_size, ctrl_data->file_dmac_descriptor_virt_buffer, ctrl_data->file_dmac_descriptor_bus_addr);
        ctrl_data->file_dmac_descriptor_size = 0;
        ctrl_data->file_dmac_descriptor_virt_buffer = NULL;
        ctrl_data->file_dmac_descriptor_bus_addr = 0;
    }
    pci_release_region(ctrl_data->pci_dev, FPGA_CTRL_BAR);
    if (atomic_read(&ctrl_data->pci_dev->enable_cnt) > 0) {
        pci_disable_device(ctrl_data->pci_dev);
    }
    if (ctrl_data->cdevno != 0) {
        unregister_chrdev_region(ctrl_data->cdevno, 1);
        if (cdev) {
            cdev_del(&ctrl_data->cdev);
        }
    }
#ifdef DEBUG
    debugfs_remove_recursive(ctrl_data->dfs_root);
#endif
    kfree(ctrl_data);
}

enum {
    FORMAT_TYPE_4K = 0,
    FORMAT_TYPE_3G
};

enum {
    PACKING_TYPE_PP = 0,
    PACKING_TYPE_CP
};

enum {
    DATA_TYPE_V = 0,
    DATA_TYPE_K
};

enum {
    DISPLAY_TYPE_NORMAL = 0,
    DISPLAY_TYPE_SEPARATE
};

static const u32 dma_type_writeport_settings[][9] = {
#ifdef SIGNAL_SELECT_4K
    { // 4K DME
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      0, // 未使用
      0, // 未使用
      0, // 未使用
      0, // 未使用
      0, // 未使用
    },
    { // 4K SLK
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
    },
#else
    { // 4K
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
    },
#endif
    { // 3G
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL   << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_SEPARATE << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      0,
    }
};

static const u32 dma_type_readport_settings[][9] = {
    { // 4K
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_CP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_CP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_4K << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
    },
    {
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_K << 1 | PACKING_TYPE_PP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_CP,
      FORMAT_TYPE_3G << 4 | DISPLAY_TYPE_NORMAL << 2 | DATA_TYPE_V << 1 | PACKING_TYPE_PP,
      0,
      0,
      0,
    }
};


static int __init sony_fpga_pci_probe(struct pci_dev* dev, const struct pci_device_id* id)
{
    int rc = 0;
    struct sony_fpga_ctrl* ctrl_data = NULL;
    struct sony_fpga_register __iomem* fpga_reg;
    void __iomem* addr;
    u8 fpga_kind;
    u8 b;
    u16 w;
    uint i = 0;
    bool pug_file_enable = false;

    for (i = 0; i < ignore_busno_num; i++) {
        if (ignore_busnos[i] == dev->bus->number) {
            dev_err(&dev->dev, "device is ignore\n");
            return -EPERM;
        }
    }

    rc = sysfs_create_group(&dev->dev.kobj, &attr_group);
    if (rc) {
        dev_info(&dev->dev,"create group failed\n");
    }

    ctrl_data = kzalloc(sizeof(struct sony_fpga_ctrl), GFP_KERNEL);
    if(!ctrl_data) {
        sysfs_remove_group(&dev->dev.kobj, &attr_group);
        sony_fpga_probe_err(NULL, false);
        return -ENOMEM;
    }

    ctrl_data->pci_dev = dev;
    pci_set_drvdata(dev, ctrl_data);

    rc = init_chrdev(ctrl_data);
    if (rc) {
        dev_err(&dev->dev, "init_chrdev() failed\n");
        sony_fpga_probe_err(ctrl_data, false);
        return rc;
    }

#ifdef DEBUG
    dfs_init(ctrl_data);
#endif

    rc = pci_enable_device(dev);
    if (rc) {
        dev_err(&dev->dev, "pci_enable_device() failed\n");
        sony_fpga_probe_err(ctrl_data, true);
        return rc;
    } else {
        dev_info(&dev->dev, "pci_enable_device() successful");
    }

    rc = pci_request_region(dev, FPGA_CTRL_BAR, SONY_FPGA_DRIVER_NAME);
    if (rc) {
        dev_err(&dev->dev, "pci_request_region() FPGA_CTRL_BAR failed\n");
        sony_fpga_probe_err(ctrl_data, true);
        return rc;
    }

    pci_set_master(dev);

    rc = pci_set_dma_mask(dev, DMA_BIT_MASK(64));
    if (!rc) {
        pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64));
        dev_info(&dev->dev, "using a 64-bit dma mask\n");
    } else {
        dev_info(&dev->dev, "unable to use 64-bit dma mask\n");
        sony_fpga_probe_err(ctrl_data, true);
        return rc;
    }

    scan_bars(ctrl_data, dev);
    map_bars(ctrl_data, dev);

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

    addr = &(fpga_reg->local_id);
    ctrl_data->local_id = IO_READ_32(addr);

    // gpu_exist
    addr = &(fpga_reg->gpu_exist);
    IO_WRITE_32(module_param_gpu_exist, addr);
    wmb();

#ifdef FPGA_UNIQ_ID_DUMMY
    ctrl_data->fpga_uniq_id = FPGA_UNIQ_ID_DUMMY;
#endif

    // GIF-VIDEO専用
#ifdef GIF_VIDEO_DUMMY
    if (1) {
#else
    if (IS_GIF_VIDEO(ctrl_data)) {
#endif
        uint i = 0;
        uint config_addr = 0;
        u8 sdi_type = 0;

        // LinkWidthのチェック
        pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &b);
        config_addr = b;
        pci_read_config_word(dev, config_addr, &w);
        for (i = 0; i < PCI_CAP_ID_MAX; i++) {
            if ((w & 0xff) != PCI_CAP_ID_EXP) {
                config_addr = w >> 8;
                if (config_addr != 0) {
                    pci_read_config_word(dev, config_addr, &w);
                } else {
                    dev_err(&dev->dev, "link status register not found.");
                    sony_fpga_probe_err(ctrl_data, true);
                    return -ENODEV;
                }
            } else {
                config_addr += PCI_EXP_LNKSTA;
                pci_read_config_word(dev, config_addr, &w);
                if ((w & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT != 16) {
                    dev_err(&dev->dev, "link width is not enough.");
                    sony_fpga_probe_err(ctrl_data, true);
                    return -ENODEV;
                } else {
                    dev_info(&dev->dev, "link width is 16");
                    break;
                }
            }
        }

        // TODO Video Descriptor作る。
        sdi_type = (module_param_video_format >> 4) & 0b1111;

        rc = alloc_video_descriptor(ctrl_data, NUM_VIDEO_DESCRIPTOR_PAGES_4K);
        if (rc) {
            dev_info(&dev->dev, "Could not allocate descriptor");
            sony_fpga_probe_err(ctrl_data, true);
            return rc;
        }

        if (sdi_type >= SDI_TYPE_6G) { // 4K
#ifdef SIGNAL_SELECT_4K
            if (module_param_gpu_assign == GPU_ASSIGN_DME || module_param_gpu_assign == GPU_ASSIGN_SLK) {
                for (i = 0; i < DMA_PORT_NUM; i++) {
                    u32 dma_type = dma_type_writeport_settings[module_param_gpu_assign][i] |
                        dma_type_readport_settings[FORMAT_TYPE_4K][i] << 16;

                    addr = &(fpga_reg->dmac_media[i].dma_type);
                    IO_WRITE_32(dma_type, addr);
                }
            } else {
                // TODO(any). SLKとDMEの共存はとりあえずない予定
            }
#else
            for (i = 0; i < DMA_PORT_NUM; i++) {
                u32 dma_type = dma_type_writeport_settings[FORMAT_TYPE_4K][i] |
                    dma_type_readport_settings[FORMAT_TYPE_4K][i] << 16;

                addr = &(fpga_reg->dmac_media[i].dma_type);
                IO_WRITE_32(dma_type, addr);
#ifdef DEBUG
                printk(KERN_INFO "%llx [%d] %x\n", (u64)addr, i, dma_type);
#endif
            }
#endif
        } else { // 3G
            for (i = 0; i < DMA_PORT_NUM; i++) {
#ifdef SIGNAL_SELECT_4K
                u32 dma_type = dma_type_writeport_settings[2][i] |
#else
                u32 dma_type = dma_type_writeport_settings[FORMAT_TYPE_3G][i] |
#endif
                    dma_type_readport_settings[FORMAT_TYPE_3G][i] << 16;

                addr = &(fpga_reg->dmac_media[i].dma_type);
                IO_WRITE_32(dma_type, addr);
            }
        }

        ctrl_data->video_desc_set = kzalloc(sizeof(struct desc_set) * VIDEO_DESC_SET_MAX, GFP_KERNEL);
        mutex_init(&ctrl_data->video_dmac_mutex);
#ifndef GPU_DISABLE
        INIT_DELAYED_WORK(&ctrl_data->video_dmac_start_work, video_dmac_start_func);
#endif
        return 0;
    }

    addr = &(fpga_reg->com_e_init);
    IO_WRITE_32(1, addr);

    // GIF-FPGAならではの処理
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_GIF && CBUSNO(ctrl_data) == module_param_gif1_busno) {
        if (!module_param_xpt_vd_enable) {
            rc = pci_enable_msi(dev);
            if (rc) {
                dev_info(&dev->dev, "pci_enable_msi() failed\n");
                ctrl_data->msi_enabled = 0;
            } else {
                dev_info(&dev->dev, "pci_enable_msi() successful\n");
                ctrl_data->msi_enabled = 1;
            }
            // GIF-FPGA Master設定
            addr = &(fpga_reg->ctrl_master_mode);
            IO_WRITE_32((module_param_slave_count << 4 | 1), addr);
            // 正誤判定エリア確保 アドレス設定
            if (process_status_area == NULL) {
                process_status_area = (u32*)pci_alloc_consistent(ctrl_data->pci_dev, PROCESS_STATUS_SIZE,
                                                         &(process_status_bus_addr));
                if (process_status_area != NULL) {
                    dev_info(&ctrl_data->pci_dev->dev, "Process Status Buffer: virt 0x%016llx bus 0x%016llx size %d\n",
                                       (u64)process_status_area,
                                       (u64)process_status_bus_addr,
                                       PROCESS_STATUS_SIZE);
                    *(u32*)process_status_area = (u32)(vd_counter & 0xffff);
                    addr = &(fpga_reg->frame_count_adrs);
                    IO_WRITE_64(process_status_bus_addr, addr);
                } else {
                    dev_err(&dev->dev, "process_status_area alloc failed\n");
                    sony_fpga_probe_err(ctrl_data, true);
                    return -ENOMEM;
                    // ERROR
                }
            }

            vd_wait_queue.initialized = false;
            vd_init_waitqueue_head(&vd_wait_queue);
            rc = request_irq(ctrl_data->pci_dev->irq, vd_isr, IRQF_SHARED, SONY_FPGA_DRIVER_NAME, (void *)ctrl_data);
            if (rc) {
                dev_err(&dev->dev, "request irq error");
                sony_fpga_probe_err(ctrl_data, true);
                return rc;
            }
        }
    }
    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        // 正誤判定エリア確保 アドレス設定
        if (process_status_area_xpt == NULL) {
            process_status_area_xpt = (u32*)pci_alloc_consistent(ctrl_data->pci_dev, PROCESS_STATUS_SIZE,
                                                     &(process_status_bus_addr_xpt));
            if (process_status_area_xpt != NULL) {
                dev_info(&ctrl_data->pci_dev->dev, "XPT Process Status Buffer: virt 0x%016llx bus 0x%016llx size %d\n",
                                   (u64)process_status_area_xpt,
                                   (u64)process_status_bus_addr_xpt,
                                   PROCESS_STATUS_SIZE);
                *(u32*)process_status_area_xpt = (u32)(vd_counter & 0xffff);
                addr = &(fpga_reg->frame_count_adrs);
                IO_WRITE_64(process_status_bus_addr_xpt, addr);
            } else {
                dev_err(&dev->dev, "xpt process_status_area alloc failed\n");
                sony_fpga_probe_err(ctrl_data, true);
                return -ENOMEM;
                // ERROR
            }
        }

        if (module_param_xpt_vd_enable) {
            rc = pci_enable_msi(dev);
            if (rc) {
                dev_info(&dev->dev, "pci_enable_msi() failed\n");
                ctrl_data->msi_enabled = 0;
            } else {
                dev_info(&dev->dev, "pci_enable_msi() successful\n");
                ctrl_data->msi_enabled = 1;
            }

            vd_wait_queue.initialized = false;
            vd_init_waitqueue_head(&vd_wait_queue);
            rc = request_irq(ctrl_data->pci_dev->irq, vd_isr, IRQF_SHARED, SONY_FPGA_DRIVER_NAME, (void *)ctrl_data);
            if (rc) {
                dev_err(&dev->dev, "request irq error");
                sony_fpga_probe_err(ctrl_data, true);
                return rc;
            }
        }
        // XPT-FPGA Master設定
        addr = &(fpga_reg->ctrl_master_mode);
        IO_WRITE_32(1, addr);
    }

    // TODO PUG Ctrl系のRead/Write設定はここでやる？（少なくともOpenではだめ）
    // 初期値を書き込むという要件がある場合はもうこの前に書き込んでしまってもいいかも。
    // VDは先に開始しておいて、サイズ0とかにしておくとアプリ起動時に余計な制御がいらずにいいかも
    // Read DMA領域はmmapを想定していたが、PUG Ctrlの仕様によっては専用Write I/Fにして
    // PUG仕様をドライバで吸収したほうがいいかも。順番も任意でいい、サイズも指定できるとなれば
    // 初期値を設定するとこにも柔軟に対応できる
    // Write DMAについては固定値で2Pageとってmmapでいいだろう。Read使うのはSelfCtrlとFM系だけになると思われる。

    fpga_kind = FPGA_KIND(ctrl_data->fpga_uniq_id);
    switch (fpga_kind) {
    case FPGA_KIND_SDI:
    case FPGA_KIND_BIM:
    case FPGA_KIND_XPT:
    case FPGA_KIND_GIF2:
    case FPGA_KIND_SIS:
        pug_file_enable = true;
        break;
    case FPGA_KIND_GIF:
        if (CBUSNO(ctrl_data) == module_param_gif1_busno) {
            pug_file_enable = true;
        }
        break;
    case FPGA_KIND_SWR:
        if (BOARD_TYPE(ctrl_data->local_id) == BOARD_TYPE_VPR149) {
            pug_file_enable = true;
        }
        break;
    default:
        break;
    }
    if (pug_file_enable) {
        u32 enable = 1;
        u32 status = 0;
        addr = &(fpga_reg->dmac_file.module_ctrl);
        IO_WRITE_32(enable, addr);
        addr = &(fpga_reg->dmac_file.module_status);
        status = IO_READ_32(addr);
        if (status & 0b1010) {
            u32 reset = 1;
            addr = &(fpga_reg->dmac_file.module_ctrl);
            IO_WRITE_32(reset, addr);
            reset = 0;
            IO_WRITE_32(reset, addr);

            status = IO_READ_32(addr);
            if (status & 0b1010) {
                dev_err(&dev->dev, "Module Status Error");
            }
        }

        rc = alloc_file_dmac_descriptor(ctrl_data, FILE_DMAC_PAGES[fpga_kind]);
        if (rc) {
            dev_info(&dev->dev, "Could not allocate file_dmac_descriptor");
            sony_fpga_probe_err(ctrl_data, true);
            return rc;
        }

        // Read/Writeは排他利用のため、descriptorも使い回し
        addr = &(fpga_reg->file_dmac_write_adrs);
        IO_WRITE_64(ctrl_data->file_dmac_descriptor_bus_addr, addr);
        addr = &(fpga_reg->file_dmac_read_adrs);
        IO_WRITE_64(ctrl_data->file_dmac_descriptor_bus_addr, addr);
    }

    if ((dev->device == SONY_FPGA_DID_NEM) ||
        (dev->device == SONY_FPGA_DID_BIM)) {
        sony_fpga_netif_notify_probe_success(ctrl_data);
    }
    // NEM以外はCTRL_DMAC有効
    if (dev->device != SONY_FPGA_DID_NEM) {
        ctrl_data->ctrl_dmac_pages = CTRL_DMAC_WRITE_DMA_PAGES +
            CTRL_DMAC_PAGES[FPGA_KIND(ctrl_data->fpga_uniq_id)][FPGA_MODE(ctrl_data->fpga_uniq_id)];

        rc = alloc_ctrl_dmac_buffer(ctrl_data);
        if (rc) {
            dev_info(&dev->dev, "Could not allocate ctrl_dmac_buffer");
            sony_fpga_probe_err(ctrl_data, true);
            return rc;
        }
        ctrl_data->ctrl_dmac_wp = (u32*)((u64)ctrl_data->ctrl_dmac_buffer + CTRL_DMAC_WRITE_DMA_PAGES * PAGE_SIZE);
        addr = &(fpga_reg->ctrl_dmac_write_adrs);
        IO_WRITE_64(ctrl_data->ctrl_dmac_buffer_bus_addr, addr);
        addr = &(fpga_reg->ctrl_dmac_read_adrs);
        IO_WRITE_64(ctrl_data->ctrl_dmac_buffer_bus_addr + CTRL_DMAC_WRITE_DMA_PAGES * PAGE_SIZE, addr);

        // Read DMAサイズは初期値未定
        addr = &(fpga_reg->ctrl_dmac_size);
        IO_WRITE_32(CTRL_DMAC_WRITE_SIZE[CTRL_DMAC_WRITE_NORMAL][fpga_kind], addr);
    }
    if (dev->device == SONY_FPGA_DID_XPT && FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        rc = sony_fpga_xpt_fpga_probe(ctrl_data);
        if (rc) {
            dev_err(&dev->dev, "xpt probe error");
            sony_fpga_probe_err(ctrl_data, true);
            return -EFAULT;
        }
    } else if (dev->device == SONY_FPGA_DID_SIS && FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_SIS && BOARD_TYPE(ctrl_data->local_id) == BOARD_TYPE_IO293 && DEV_ID(ctrl_data->local_id) == 0) {
        rc = sony_fpga_sis_fpga_probe(ctrl_data);
        if (rc) {
            dev_err(&dev->dev, "sis probe error");
            sony_fpga_probe_err(ctrl_data, true);
            return -EFAULT;
        }
    }

    mutex_init(&ctrl_data->ctrl_dmac_mutex);
    mutex_init(&ctrl_data->file_dmac_mutex);
    mutex_init(&ctrl_data->nios_mutex);

    return 0;
}

static void __exit sony_fpga_pci_remove(struct pci_dev* dev)
{
    struct sony_fpga_ctrl* ctrl_data = NULL;
    struct sony_fpga_register __iomem* fpga_reg;
    void __iomem* addr;
    ctrl_data = pci_get_drvdata(dev);

#ifdef DEBUG
    debugfs_remove_recursive(ctrl_data->dfs_root);
#endif

#ifdef GIF_VIDEO_DUMMY
    if (1) {
#else
    if (dev->device == SONY_FPGA_DID_VIDEO) {
#endif

#ifndef GPU_DISABLE
        int ret = cancel_delayed_work_sync(&ctrl_data->video_dmac_start_work);
        pr_debug("video_dmac_start_work cancel rc %d\n",ret);
#endif
        // GIF-Video専用処理
        // Video Descriptor片付け
        kfree(ctrl_data->video_desc_set);
    } else {
        fpga_reg = (struct sony_fpga_register __iomem*)ctrl_data->bar[FPGA_CTRL_BAR];
        addr = &(fpga_reg->irq_vd_control);
        IO_WRITE_32(0, addr);
        wmb();

        if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_GIF && CBUSNO(ctrl_data) == module_param_gif1_busno) {
            if (process_status_area != NULL) {
                pci_free_consistent(dev, PROCESS_STATUS_SIZE, process_status_area, process_status_bus_addr);
            }
            process_status_area = NULL;
            process_status_bus_addr = 0;
            // GIF-FPGAのみ
            if (!module_param_xpt_vd_enable) {
                free_irq(ctrl_data->pci_dev->irq, (void*)ctrl_data);
                dev_info(&ctrl_data->pci_dev->dev, "gif free irq\n");
                pci_disable_msi(dev);
            }
        } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
            if (process_status_area_xpt != NULL) {
                pci_free_consistent(dev, PROCESS_STATUS_SIZE, process_status_area_xpt, process_status_bus_addr_xpt);
            }
            process_status_area_xpt = NULL;
            process_status_bus_addr_xpt = 0;
            if (module_param_xpt_vd_enable) {
                free_irq(ctrl_data->pci_dev->irq, (void*)ctrl_data);
                dev_info(&ctrl_data->pci_dev->dev, "xpt free irq\n");
                pci_disable_msi(dev);
            }

        }

        free_ctrl_dmac_buffer(ctrl_data);
    }

    if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_XPT) {
        u32 i = 0;
        for (i = 0; i < I2C_RTM; i++) {
            if (ctrl_data->qsfp_ctrl[i].poll_thread != NULL) {
                kthread_stop(ctrl_data->qsfp_ctrl[i].poll_thread);
            }
        }
        kfree(ctrl_data->qsfp_ctrl);
    } else if (FPGA_KIND(ctrl_data->fpga_uniq_id) == FPGA_KIND_SIS) {
        if (ctrl_data->qsfp_ctrl != NULL) {
            if (ctrl_data->qsfp_ctrl->poll_thread != NULL) {
                kthread_stop(ctrl_data->qsfp_ctrl->poll_thread);
            }
            kfree(ctrl_data->qsfp_ctrl);
        }
    }

    if (ctrl_data->file_dmac_descriptor_virt_buffer) {
        pci_free_consistent(dev, ctrl_data->file_dmac_descriptor_size, ctrl_data->file_dmac_descriptor_virt_buffer, ctrl_data->file_dmac_descriptor_bus_addr);
        ctrl_data->file_dmac_descriptor_size = 0;
        ctrl_data->file_dmac_descriptor_virt_buffer = NULL;
        ctrl_data->file_dmac_descriptor_bus_addr = 0;
    }

    sysfs_remove_group(&dev->dev.kobj, &attr_group);

    unmap_bars(ctrl_data, dev);

    pci_clear_master(dev);

    pci_release_regions(dev);
    pci_disable_device(dev);
    cdev_del(&ctrl_data->cdev);
    unregister_chrdev_region(ctrl_data->cdevno, 1);

    dev_info(&ctrl_data->pci_dev->dev, SONY_FPGA_DRIVER_NAME ": sony_fpga_remove()\n");
    pci_set_drvdata(dev, NULL);
    kfree(ctrl_data);
}

static struct pci_device_id pci_ids[] = {
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_VIDEO) },
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_ULM)},
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_NEM)   },
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_BIM)   },
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_XPT)   },
    { PCI_DEVICE(SONY_FPGA_VID, SONY_FPGA_DID_SIS)   },
    { 0 }
};

MODULE_DEVICE_TABLE(pci, pci_ids);

static struct pci_driver sony_fpga_driver_ops = {
    .name     = SONY_FPGA_DRIVER_NAME,
    .id_table = pci_ids,
    .probe    = sony_fpga_pci_probe,
    .remove   = sony_fpga_pci_remove,
};

static int __init sony_fpga_init(void)
{
    return pci_register_driver(&sony_fpga_driver_ops);
}

static void __exit sony_fpga_cleanup(void)
{
    pci_unregister_driver(&sony_fpga_driver_ops);
}

module_init(sony_fpga_init);
module_exit(sony_fpga_cleanup);


