/***************************************************************************/
/*                             Druckersoftware                             */
/*                    Copyright (C) Carl Valentin GmbH                     */
/***************************************************************************/

/*
 * dmaFramework.h
 *
 *  Created on: 11.03.2022
 *      Author: preitinger
 */

#ifndef DMAFRAMEWORK_DMAFRAMEWORK_H_
#define DMAFRAMEWORK_DMAFRAMEWORK_H_

#include "../Edma_neu.h"
#include "dmaAddresses.h"





static int startCopyPs, optHiPs, delayPs, chainPayloadPs, ackPs;

static void dumpPsNum(const char* name, int ps) __attribute((unused));

static void dumpPsNum(const char* name, int ps) {
    pr_info("%s\t%d\t(0x%x)\n", name, ps, ps << 5);
}

static int dmaFramework_init(struct device* dev) {
    int res = 0;
    int start1ChanNum, start1Ps;

//    pr_info("dmaFramework_init\n");

    dmaengine_get();

//    pr_info("vor dma_request_chan fuer start\n");
    start1Chan = dma_request_chan(dev, "start");
    start1ChanNum = edma_channel_num(start1Chan);
    start1Ps = paramSetOfChannel(start1ChanNum);

    if (unlikely(IS_ERR(start1Chan))) {
        pr_err("dma_request_chan fuer start gescheitert: %ld\n", PTR_ERR(start1Chan));
        res = (int) PTR_ERR(start1Chan);
        goto err_start;
    }

//    pr_info("(das ist nicht die edma-nr!) start1Chan->chan_id=%d\n", start1Chan->chan_id);

//    pr_info("vor dma_request_chan fuer adc\n");
    adcChan = dma_request_chan(dev, "adc");

    if (unlikely(IS_ERR(adcChan))) {
        pr_err("dma_request_chan fuer adc gescheitert: %ld\n", PTR_ERR(adcChan));
        res = (int) PTR_ERR(adcChan);
        goto err_adc;
    }

//    pr_info("(das ist nicht die edma-nr!) adcChan->chan_id=%d.\n", adcChan->chan_id);


    startCopyPs = my_edma_alloc_slot(-1);

    if (unlikely(startCopyPs < 0)) {
        res = -1;
        goto startCopy_err;
    }


    optHiPs = my_edma_alloc_slot(-1);

    if (unlikely(optHiPs < 0)) {
        res = -1;
        goto optHi_err;
    }

    delayPs = my_edma_alloc_slot(-1);

    if (unlikely(delayPs < 0)) {
        res = -1;
        goto delay_err;
    }

    chainPayloadPs = my_edma_alloc_slot(-1);

    if (unlikely(chainPayloadPs < 0)) {
        res = -1;
        goto chainPayload_err;
    }

    ackPs = my_edma_alloc_slot(-1);

    if (unlikely(ackPs < 0)) {
        res = -1;
        goto ack_err;
    }

//    pr_info("start1ChanNum   \t%d\n", start1ChanNum);
//    dumpPsNum("start1Ps      ", start1Ps);
//    dumpPsNum("startCopyPs   ", startCopyPs);
//    dumpPsNum("optHiPs   ", optHiPs);
//    dumpPsNum("delayPs   ", delayPs);
//    dumpPsNum("chainPayloadPs", chainPayloadPs);
//    dumpPsNum("ackPs         ", ackPs);

    // channels und parametersaetze programmieren; ab hier wohl keine fehlersituation mehr...

    {
        struct edmacc_param ps = {
#if 1
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum) | TCCHEN,
#else // testweise ohne TCCHEN
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum),
#endif
                .src = FPGA_ADDR_BASE | ADDR_READ_DMA_TCC_LO,
                .a_b_cnt = 2 | (1 << 16),
                .dst = EDMA_BASE | PARM_OFFSET(chainPayloadPs), // opt-Feld gleich am Anfang, daher dafuer kein offset
                .src_dst_bidx = 0,
                .link_bcntrld = (optHiPs << 5),
                .src_dst_cidx = 0,
#if 1
                .ccnt = 1
#else // zum Test dummy
                .ccnt = 0
#endif
        };

        my_edma_write_slot(edmaMappedIo1._a, startCopyPs, &ps);
        my_edma_write_slot(edmaMappedIo1._a, start1Ps, &ps);
    }

    {
        struct edmacc_param ps = {
#if 1
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum) | TCCHEN,
#else // testweise ohne TCCHEN
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum),
#endif
                .src = FPGA_ADDR_BASE | ADDR_READ_DMA_TCC_HI, // opt-Feld gleich am Anfang, daher dafuer kein offset
                .a_b_cnt = 2 | (1 << 16),
                .dst = EDMA_BASE | PARM_OFFSET(chainPayloadPs) | 2,
                .src_dst_bidx = 0,
                .link_bcntrld = (delayPs << 5),
                .src_dst_cidx = 0,
#if 1
                .ccnt = 1
#else // zum Test dummy
                .ccnt = 0
#endif
        };

        my_edma_write_slot(edmaMappedIo1._a, optHiPs, &ps);
    }

    {
        struct edmacc_param ps = {
#if 1
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum) | TCCHEN,
#else // testweise ohne TCCHEN
                .opt = SYNCDIM | EDMA_TCC(start1ChanNum),
#endif
                     .src = 0,
                .a_b_cnt = 0,
                .dst = 0,
                .src_dst_bidx = 0,
                .link_bcntrld = (chainPayloadPs << 5),
                .src_dst_cidx = 0,
                .ccnt = 1
        };

        my_edma_write_slot(edmaMappedIo1._a, delayPs, &ps);
    }

    {
        struct edmacc_param ps = {
                // opt kommt ja dynamisch vom FPGA durch erstes Kettenglied (start1Ps)
                .src = 0,
                .a_b_cnt = 0,
                .dst = 0,
                .src_dst_bidx = 0,
                .link_bcntrld = (ackPs << 5),
                .src_dst_cidx = 0,
                .ccnt = 1
        };

        my_edma_write_slot(edmaMappedIo1._a, chainPayloadPs, &ps);
    }

    {
        struct edmacc_param ps = {
                .opt = EDMA_TCC(start1ChanNum), // eigentlich ueberfluessig, aber wohl in den edma-treibern ueblich dass man auch ohne chaining und interrupt dann den eigenen paramset setzt.
                .src = FPGA_ADDR_BASE | ADDR_READ_GENERIC_DMA_FIFO,
                .a_b_cnt = (2 | (1 << 16)),
                .dst = FPGA_ADDR_BASE | ADDR_OTH_GENERIC_DMA_ACK,
                .src_dst_bidx = 0,
                .link_bcntrld = (startCopyPs << 5),
                .src_dst_cidx = 0,
                .ccnt = 1
        };

        my_edma_write_slot(edmaMappedIo1._a, ackPs, &ps);
    }

    {
        // enable event for start1Chan
        int start1Num = edma_channel_num(start1Chan);
        ASSERT(start1Num >= 0 && start1Num < 64);
//        pr_info("start1Num: %d\n", start1Num);
//        pr_info("EER vor enable event fuer start1Chan: 0x%x\n", DmaChan_MappedIo_get32(&edmaMappedIo1, 0x1020));
//        pr_info("EERH vor enable event fuer start1Chan: 0x%x\n", DmaChan_MappedIo_get32(&edmaMappedIo1, 0x1024));
        DmaChan_MappedIo_set32(&edmaMappedIo1,
                               (start1Num < 32 ? 0x1030 : 0x1034),
                               1 << (start1Num < 32 ? start1Num : start1Num - 32));
//        pr_info("EER nach enable event fuer start1Chan: 0x%x\n", DmaChan_MappedIo_get32(&edmaMappedIo1, 0x1020));
//        pr_info("EERH nach enable event fuer start1Chan: 0x%x\n", DmaChan_MappedIo_get32(&edmaMappedIo1, 0x1024));
    }

    goto success;


    ack_err:
    my_edma_free_slot(chainPayloadPs);

    chainPayload_err:
    my_edma_free_slot(delayPs);

    delay_err:
    my_edma_free_slot(optHiPs);

    optHi_err:
    my_edma_free_slot(startCopyPs);

    startCopy_err:
    dma_release_channel(adcChan);

    err_adc:
    dma_release_channel(start1Chan);

    err_start:
    dmaengine_put();

    success:
    return res;
}

static void dmaFramework_release(void) {
//    pr_info("dmaFramework_release\n");

    my_edma_free_slot(ackPs);

    my_edma_free_slot(chainPayloadPs);

    my_edma_free_slot(delayPs);

    my_edma_free_slot(optHiPs);

    my_edma_free_slot(startCopyPs);

    dma_release_channel(adcChan);

    dma_release_channel(start1Chan);

    dmaengine_put();

}

#endif /* DMAFRAMEWORK_DMAFRAMEWORK_H_ */
