/*
 * File      : drv_dma.c
 * This file is part of gkipc BSP for RT-Thread distribution.
 *
 * Copyright (c) 2017 ChengDu goke Microelectronics Co., Ltd.
 * All rights reserved
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Visit http://www.goke.com to get contact with goke.
 *
 * Change Logs:
 * Date           Author       Notes
 */

/*****************************************************************************
 *  Include Section
 *  add all #include here
 *****************************************************************************/
#include "drivers/drv_dma.h"

#include "gd_dma.h"

/*****************************************************************************
 * Define section
 * add all #define here
 *****************************************************************************/

//#define GK_DMA_DEBUG
#ifndef GK_DMA_DEBUG
#define DMA_PRINT_DBG(fmt, args...)
#define DMA_PRINT_ERR(fmt, args...) rt_kprintf(fmt, ##args);
#else
#define DMA_PRINT_DBG(fmt, args...) rt_kprintf(fmt, ##args);
#define DMA_PRINT_ERR(fmt, args...) rt_kprintf(fmt, ##args);
#endif

/****************************************************************************
 * ADT section
 *  add definition of user defined Data Type that only be used in this file here
 ***************************************************************************/

static struct rt_dma_device _dma_device;


/******************************************************************************
 * Function prototype section
 * add prototypes for all functions called by this file,execepting those
 * declared in header file
 *****************************************************************************/

/*****************************************************************************
 * Global variables section - Exported
 * add declaration of global variables that will be exported here
 * e.g.
 *  int8_t foo;
 ****************************************************************************/

/*****************************************************************************

 *  static fun;
 *****************************************************************************/
static rt_err_t rt_dma_init(struct rt_device *dev);
static rt_err_t rt_dma_open(struct rt_device *dev, rt_uint16_t oflag);
static rt_err_t rt_dma_close(struct rt_device *dev);
static rt_err_t rt_dma_control(struct rt_device *dev, int cmd,void *args);
/*****************************************************************************
 * Global variables section - Local
 * define global variables(will be refered only in this file) here,
 * static keyword should be used to limit scope of local variable to this file
 * e.g.
 *  static uint8_t ufoo;
 *****************************************************************************/

/* function body */
/*****************************************************************************
 * Description:
 *      add funtion description here
 * Parameters:
 *      description for each argument, new argument starts at new line
 * Return:
 *      what does this function returned?
 *****************************************************************************/
static rt_err_t rt_dma_init(struct rt_device *dev)
{
    return (RT_EOK);
}

static rt_err_t rt_dma_open(struct rt_device *dev, rt_uint16_t oflag)
{
    rt_uint32_t retVal = 0;
    struct rt_dma_device *dma;

    RT_ASSERT(dev != RT_NULL);
    dma = (struct rt_dma_device *)dev->user_data;

    retVal = GD_DMA_Open((GD_DMA_OPEN_PARAM_S *)&dma->openParam,(GD_HANDLE *)&dma->handle);
    if(retVal != RT_EOK)
    {
        DMA_PRINT_ERR("GD_DMA_Open failed!\n");
        return (-RT_ERROR);
    }

    return (RT_EOK);
}

static rt_err_t rt_dma_close(struct rt_device *dev)
{
    rt_uint32_t retVal = 0;
    struct rt_dma_device *dma;

    RT_ASSERT(dev != RT_NULL);
    dma = (struct rt_dma_device *)dev->user_data;

    GD_DMA_Stop((GD_HANDLE)dma->handle);

    retVal = GD_DMA_Close((GD_HANDLE)dma->handle);
    if (retVal != RT_EOK)
    {
        DMA_PRINT_ERR("GD_DMA_Close failed!\n");
        return (-RT_ERROR);
    }

    return (RT_EOK);
}

static rt_err_t rt_dma_control(struct rt_device *dev, int cmd,void *args)
{
    rt_uint32_t retVal = 0;
    rt_uint32_t des    = 0;
    RT_DMA_DESCRIPTOR_S *descriptor;

    struct rt_dma_device *dma;
    RT_ASSERT(dev != RT_NULL);
    dma = (struct rt_dma_device *)dev->user_data;

    switch(cmd)
    {
        case DMA_CMD_ADD_DESCRIPTOR:
            descriptor = (RT_DMA_DESCRIPTOR_S *)args;
            retVal = GD_DMA_AddDescriptor((GD_HANDLE)dma->handle,(GD_DMA_DESCRIPTOR_S *)descriptor);
            if (retVal != RT_EOK)
            {
                DMA_PRINT_ERR("GD_DMA_AddDescriptor failed!\n");
                return (-RT_ERROR);
            }

            break;

        case DMA_CMD_START:
            des = *(rt_uint32_t*)args;
            retVal = GD_DMA_Start((GD_HANDLE)dma->handle,des);
            if (retVal != RT_EOK)
            {
                DMA_PRINT_ERR("GD_DMA_Start failed!\n");
                return (-RT_ERROR);
            }

            break;

        case DMA_CMD_STOP:
            GD_DMA_Stop((GD_HANDLE)dma->handle);
            break;

        default:break;


    }

    return (RT_EOK);
}

/**
 * This function register a dma device
 */
static rt_err_t rt_hw_dma_register(const char *name,rt_uint32_t flag)
{
    rt_uint32_t ret;
    struct rt_device *device;

    device = &(_dma_device.parent);

    device->type        = RT_Device_Class_Miscellaneous;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    device->init      = rt_dma_init;
    device->open      = rt_dma_open;
    device->close     = rt_dma_close;
    device->read      = RT_NULL;
    device->write     = RT_NULL;
    device->control   = rt_dma_control;
    device->user_data = (void *)&_dma_device;

    /* register a character device */
    ret = rt_device_register(device, name, flag);

    return ret;
}

void rt_gk_dma_init(void)
{
    rt_uint32_t retVal = 0;

    retVal = GD_DMA_Init();
    if (retVal != RT_EOK)
    {
        DMA_PRINT_ERR("GD_DMA_Init failed!\n");
		return;
    }

    rt_hw_dma_register("gk_dma",RT_DEVICE_FLAG_RDWR);
}