diff --git a/driver/axidma_dma.c b/driver/axidma_dma.c index 864c9fc..19568e3 100644 --- a/driver/axidma_dma.c +++ b/driver/axidma_dma.c @@ -120,7 +120,7 @@ static int axidma_init_sg_entry(struct axidma_device *dev, } static struct axidma_chan *axidma_get_chan(struct axidma_device *dev, - int channel_id, enum axidma_type type, enum axidma_dir dir) + int channel_id) { int i; struct axidma_chan *chan; @@ -129,8 +129,7 @@ static struct axidma_chan *axidma_get_chan(struct axidma_device *dev, for (i = 0; i < dev->num_chans; i++) { chan = &dev->channels[i]; - if (chan->channel_id == channel_id && chan->type == type && - chan->dir == dir) { + if (chan->channel_id == channel_id) { return chan; } } @@ -160,13 +159,14 @@ static void axidma_dma_callback(void *data) // Setup the config structure for VDMA static void axidma_setup_vdma_config(struct xilinx_vdma_config *dma_config) { + memset(dma_config, 0, sizeof(dma_config)); dma_config->frm_dly = 0; // Number of frames to delay - dma_config->gen_lock = 0; // No genlock, VDMA runs freely + dma_config->gen_lock = 0; // Genlock, VDMA runs on fsyncs dma_config->master = 0; // VDMA is the genlock master - dma_config->frm_cnt_en = 0; // No interrupts based on frame count + dma_config->frm_cnt_en = 1; // Interrupt based on frame count dma_config->park = 0; // Continuously process all frames dma_config->park_frm = 0; // Frame to stop (park) at (N/A) - dma_config->coalesc = 0; // No transfer completion interrupts + dma_config->coalesc = 1; // Interrupt after one frame completion dma_config->delay = 0; // Disable the delay counter interrupt dma_config->reset = 0; // Don't reset the channel dma_config->ext_fsync = 0; // VDMA handles synchronizes itself @@ -182,6 +182,7 @@ static int axidma_prep_transfer(struct axidma_chan *axidma_chan, struct completion *dma_comp; struct xilinx_vdma_config vdma_config; struct axidma_cb_data *cb_data; + struct dma_interleaved_template dma_template; enum dma_transfer_direction dma_dir; enum dma_ctrl_flags dma_flags; struct scatterlist *sg_list; @@ -201,21 +202,32 @@ static int axidma_prep_transfer(struct axidma_chan *axidma_chan, type = axidma_type_to_string(dma_tfr->type); cb_data = dma_tfr->cb_data; - // If it's a VDMA transaction, configure it based on a default - if (dma_tfr->type == AXIDMA_VDMA) { + /* For VDMA transfers, we configure the channel, then prepare an interlaved + * transfer. For DMA, we simply prepare a slave scatter-gather transfer. */ + dma_flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + if (dma_tfr->type == AXIDMA_DMA) { + dma_txnd = dmaengine_prep_slave_sg(chan, sg_list, sg_len, dma_dir, + dma_flags); + } else { axidma_setup_vdma_config(&vdma_config); rc = xilinx_vdma_channel_set_config(chan, &vdma_config); if (rc < 0) { axidma_err("Unable to set the config for channel.\n"); goto stop_dma; } - } - /* Configure the engine to send an interrupt acknowledgement upon - * completion, and skip unmapping the buffer. */ - dma_flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; - dma_txnd = dmaengine_prep_slave_sg(chan, sg_list, sg_len, dma_dir, - dma_flags); + memset(&dma_template, 0, sizeof(dma_template)); + dma_template.dst_start = sg_dma_address(&sg_list[0]); + dma_template.src_start = sg_dma_address(&sg_list[0]); + dma_template.dir = dma_dir; + dma_template.numf = dma_tfr->vdma_tfr.height; + dma_template.frame_size = 1; + dma_template.sgl[0].size = dma_tfr->vdma_tfr.width * + dma_tfr->vdma_tfr.depth; + dma_template.sgl[0].icg = 0; + dma_txnd = dmaengine_prep_interleaved_dma(chan, &dma_template, + dma_flags); + } if (dma_txnd == NULL) { axidma_err("Unable to prepare the dma engine for the %s %s buffer.\n", type, direction); @@ -343,34 +355,33 @@ int axidma_read_transfer(struct axidma_device *dev, int rc; struct axidma_chan *rx_chan; struct scatterlist sg_list; + struct axidma_transfer rx_tfr; - // Setup receive transfer structure for DMA - struct axidma_transfer rx_tfr = { - .sg_list = &sg_list, - .sg_len = 1, - .dir = AXIDMA_READ, - .type = AXIDMA_DMA, - .wait = trans->wait, - .channel_id = trans->channel_id, - .notify_signal = dev->notify_signal, - .process = get_current(), - }; + // Get the channel with the given channel id + rx_chan = axidma_get_chan(dev, trans->channel_id); + if (rx_chan == NULL || rx_chan->dir != AXIDMA_READ) { + axidma_err("Invalid device id %d for DMA receive channel.\n", + trans->channel_id); + return -ENODEV; + } // Setup the scatter-gather list for the transfer (only one entry) - sg_init_table(rx_tfr.sg_list, rx_tfr.sg_len); - rc = axidma_init_sg_entry(dev, rx_tfr.sg_list, 0, trans->buf, + sg_init_table(&sg_list, 1); + rc = axidma_init_sg_entry(dev, &sg_list, 0, trans->buf, trans->buf_len); if (rc < 0) { return rc; } - // Get the channel with the given channel id - rx_chan = axidma_get_chan(dev, trans->channel_id, AXIDMA_DMA, AXIDMA_READ); - if (rx_chan == NULL) { - axidma_err("Invalid device id %d for DMA receive channel.\n", - trans->channel_id); - return -ENODEV; - } + // Setup receive transfer structure for DMA + rx_tfr.sg_list = &sg_list; + rx_tfr.sg_len = 1; + rx_tfr.dir = rx_chan->dir; + rx_tfr.type = rx_chan->type; + rx_tfr.wait = trans->wait; + rx_tfr.channel_id = trans->channel_id; + rx_tfr.notify_signal = dev->notify_signal; + rx_tfr.process = get_current(); rx_tfr.cb_data = &dev->cb_data[trans->channel_id]; // Prepare the receive transfer @@ -394,34 +405,33 @@ int axidma_write_transfer(struct axidma_device *dev, int rc; struct axidma_chan *tx_chan; struct scatterlist sg_list; + struct axidma_transfer tx_tfr; - // Setup transmit transfer structure for DMA - struct axidma_transfer tx_tfr = { - .sg_list = &sg_list, - .sg_len = 1, - .dir = AXIDMA_WRITE, - .type = AXIDMA_DMA, - .wait = trans->wait, - .channel_id = trans->channel_id, - .notify_signal = dev->notify_signal, - .process = get_current(), - }; + // Get the channel with the given id + tx_chan = axidma_get_chan(dev, trans->channel_id); + if (tx_chan == NULL || tx_chan->dir != AXIDMA_WRITE) { + axidma_err("Invalid device id %d for DMA transmit channel.\n", + trans->channel_id); + return -ENODEV; + } // Setup the scatter-gather list for the transfer (only one entry) - sg_init_table(tx_tfr.sg_list, tx_tfr.sg_len); - rc = axidma_init_sg_entry(dev, tx_tfr.sg_list, 0, trans->buf, + sg_init_table(&sg_list, 1); + rc = axidma_init_sg_entry(dev, &sg_list, 0, trans->buf, trans->buf_len); if (rc < 0) { return rc; } - // Get the channel with the given id - tx_chan = axidma_get_chan(dev, trans->channel_id, AXIDMA_DMA, AXIDMA_WRITE); - if (tx_chan == NULL) { - axidma_err("Invalid device id %d for DMA transmit channel.\n", - trans->channel_id); - return -ENODEV; - } + // Setup transmit transfer structure for DMA + tx_tfr.sg_list = &sg_list; + tx_tfr.sg_len = 1; + tx_tfr.dir = tx_chan->dir; + tx_tfr.type = tx_chan->type; + tx_tfr.wait = trans->wait; + tx_tfr.channel_id = trans->channel_id; + tx_tfr.notify_signal = dev->notify_signal; + tx_tfr.process = get_current(); tx_tfr.cb_data = &dev->cb_data[trans->channel_id]; // Prepare the transmit transfer @@ -447,61 +457,64 @@ int axidma_rw_transfer(struct axidma_device *dev, int rc; struct axidma_chan *tx_chan, *rx_chan; struct scatterlist tx_sg_list, rx_sg_list; + struct axidma_transfer tx_tfr, rx_tfr; - // Setup receive and trasmit transfer structures for DMA - struct axidma_transfer tx_tfr = { - .sg_list = &tx_sg_list, - .sg_len = 1, - .dir = AXIDMA_WRITE, - .type = AXIDMA_DMA, - .wait = false, - .channel_id = trans->tx_channel_id, - .notify_signal = dev->notify_signal, - .process = get_current(), - }; - struct axidma_transfer rx_tfr = { - .sg_list = &rx_sg_list, - .sg_len = 1, - .dir = AXIDMA_READ, - .type = AXIDMA_DMA, - .wait = trans->wait, - .channel_id = trans->rx_channel_id, - .notify_signal = dev->notify_signal, - .process = get_current(), - }; + // Get the transmit and receive channels with the given ids. + tx_chan = axidma_get_chan(dev, trans->tx_channel_id); + if (tx_chan == NULL || tx_chan->dir != AXIDMA_WRITE) { + axidma_err("Invalid device id %d for DMA transmit channel.\n", + trans->tx_channel_id); + return -ENODEV; + } + + rx_chan = axidma_get_chan(dev, trans->rx_channel_id); + if (rx_chan == NULL || rx_chan->dir != AXIDMA_READ) { + axidma_err("Invalid device id %d for DMA receive channel.\n", + trans->rx_channel_id); + return -ENODEV; + } // Setup the scatter-gather list for the transfers (only one entry) - sg_init_table(tx_tfr.sg_list, tx_tfr.sg_len); - rc = axidma_init_sg_entry(dev, tx_tfr.sg_list, 0, trans->tx_buf, + sg_init_table(&tx_sg_list, 1); + rc = axidma_init_sg_entry(dev, &tx_sg_list, 0, trans->tx_buf, trans->tx_buf_len); if (rc < 0) { return rc; } - sg_init_table(rx_tfr.sg_list, rx_tfr.sg_len); - rc = axidma_init_sg_entry(dev, rx_tfr.sg_list, 0, trans->rx_buf, + sg_init_table(&rx_sg_list, 1); + rc = axidma_init_sg_entry(dev, &rx_sg_list, 0, trans->rx_buf, trans->rx_buf_len); if (rc < 0) { return rc; } - // Get the transmit and receive channels with the given ids. - tx_chan = axidma_get_chan(dev, trans->tx_channel_id, AXIDMA_DMA, - AXIDMA_WRITE); - if (tx_chan == NULL) { - axidma_err("Invalid device id %d for DMA transmit channel.\n", - trans->tx_channel_id); - return -ENODEV; - } + // Setup receive and trasmit transfer structures for DMA + tx_tfr.sg_list = &tx_sg_list, + tx_tfr.sg_len = 1, + tx_tfr.dir = tx_chan->dir, + tx_tfr.type = tx_chan->type, + tx_tfr.wait = false, + tx_tfr.channel_id = trans->tx_channel_id, + tx_tfr.notify_signal = dev->notify_signal, + tx_tfr.process = get_current(), tx_tfr.cb_data = &dev->cb_data[trans->tx_channel_id]; - - rx_chan = axidma_get_chan(dev, trans->rx_channel_id, AXIDMA_DMA, - AXIDMA_READ); - if (rx_chan == NULL) { - axidma_err("Invalid device id %d for DMA receive channel.\n", - trans->rx_channel_id); - return -ENODEV; - } + // FIXME: FIXME: FIXME: Temporary + tx_tfr.vdma_tfr.height = 1080; + tx_tfr.vdma_tfr.width = 1920; + tx_tfr.vdma_tfr.depth = 4; + + rx_tfr.sg_list = &rx_sg_list, + rx_tfr.sg_len = 1, + rx_tfr.dir = rx_chan->dir, + rx_tfr.type = rx_chan->type, + rx_tfr.wait = trans->wait, + rx_tfr.channel_id = trans->rx_channel_id, + rx_tfr.notify_signal = dev->notify_signal, + rx_tfr.process = get_current(), rx_tfr.cb_data = &dev->cb_data[trans->rx_channel_id]; + rx_tfr.vdma_tfr.height = 1080; + rx_tfr.vdma_tfr.width = 1920; + rx_tfr.vdma_tfr.depth = 4; // Prep both the receive and transmit transfers rc = axidma_prep_transfer(tx_chan, &tx_tfr); @@ -568,10 +581,10 @@ int axidma_video_write_transfer(struct axidma_device *dev, } // Get the channel with the given id - tx_chan = axidma_get_chan(dev, trans->channel_id, AXIDMA_DMA, - AXIDMA_WRITE); - if (tx_chan == NULL) { - axidma_err("Invalid device id %d for DMA transmit channel.\n", + tx_chan = axidma_get_chan(dev, trans->channel_id); + if (tx_chan == NULL && tx_chan->dir != AXIDMA_WRITE && + tx_chan->type != AXIDMA_VDMA) { + axidma_err("Invalid device id %d for VDMA transmit channel.\n", trans->channel_id); rc = -ENODEV; goto free_sg_list; @@ -599,9 +612,9 @@ int axidma_stop_channel(struct axidma_device *dev, struct axidma_chan *chan; // Get the transmit and receive channels with the given ids. - chan = axidma_get_chan(dev, chan_info->channel_id, chan_info->type, - chan_info->dir); - if (chan == NULL) { + chan = axidma_get_chan(dev, chan_info->channel_id); + if (chan == NULL && chan->type != chan_info->type && + chan->dir != chan_info->dir) { axidma_err("Invalid channel id %d for %s %s channel.\n", chan_info->channel_id, axidma_type_to_string(chan_info->type), axidma_dir_to_string(chan_info->dir)); diff --git a/examples/axidma_benchmark.c b/examples/axidma_benchmark.c index 58be3f5..4da429c 100644 --- a/examples/axidma_benchmark.c +++ b/examples/axidma_benchmark.c @@ -69,7 +69,7 @@ static void print_usage(bool help) FILE* stream = (help) ? stdout : stderr; double default_size; - fprintf(stream, "Usage: axidma_benchmark [-t ] " + fprintf(stream, "Usage: axidma_benchmark [-v] [-t ] " "[-r ] [-i ] " "[-b ] [-o ] [-n ]\n"); @@ -78,6 +78,8 @@ static void print_usage(bool help) } default_size = BYTE_TO_MB(DEFAULT_TRANSFER_SIZE); + fprintf(stream, "\t-v:\t\t\t\tUse the AXI VDMA channels instead of AXI DMA " + "ones for the transfer.\n"); fprintf(stream, "\t-t :\t\t\tThe device id of the DMA " "channel to use for transmitting the data to the PL fabric.\n"); fprintf(stream, "\t-r :\t\t\tThe device id of the DMA " @@ -103,23 +105,28 @@ static void print_usage(bool help) /* Parses the command line arguments overriding the default transfer sizes, * and number of transfer to use for the benchmark if specified. */ static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel, - size_t *tx_size, size_t *rx_size, int *num_transfers) + size_t *tx_size, size_t *rx_size, int *num_transfers, bool *use_vdma) { double double_arg; int int_arg; char option; // Set the default data size and number of transfers + *use_vdma = false; *tx_channel = -1; *rx_channel = -1; *tx_size = DEFAULT_TRANSFER_SIZE; *rx_size = DEFAULT_TRANSFER_SIZE; *num_transfers = DEFAULT_NUM_TRANSFERS; - while ((option = getopt(argc, argv, "t:r:i:b:o:s:n:h")) != (char)-1) + while ((option = getopt(argc, argv, "vt:r:i:b:o:s:n:h")) != (char)-1) { switch (option) { + case 'v': + *use_vdma = true; + break; + // Parse the transmit channel argument case 't': if (parse_int(option, optarg, &int_arg) < 0) { @@ -399,13 +406,14 @@ int main(int argc, char **argv) int num_transfers; int tx_channel, rx_channel; size_t tx_size, rx_size; + bool use_vdma; char *tx_buf, *rx_buf; axidma_dev_t axidma_dev; const array_t *tx_chans, *rx_chans; // Check if the user overrided the default transfer size and number if (parse_args(argc, argv, &tx_channel, &rx_channel, &tx_size, &rx_size, - &num_transfers) < 0) { + &num_transfers, &use_vdma) < 0) { rc = 1; goto ret; } @@ -437,13 +445,18 @@ int main(int argc, char **argv) } // Get all the transmit and receive channels - tx_chans = axidma_get_dma_tx(axidma_dev); + if (use_vdma) { + tx_chans = axidma_get_vdma_tx(axidma_dev); + rx_chans = axidma_get_vdma_rx(axidma_dev); + } else { + tx_chans = axidma_get_dma_tx(axidma_dev); + rx_chans = axidma_get_dma_rx(axidma_dev); + } if (tx_chans->len < 1) { fprintf(stderr, "Error: No transmit channels were found.\n"); rc = -ENODEV; goto free_rx_buf; } - rx_chans = axidma_get_dma_rx(axidma_dev); if (rx_chans->len < 1) { fprintf(stderr, "Error: No receive channels were found.\n"); rc = -ENODEV; @@ -467,7 +480,6 @@ int main(int argc, char **argv) } printf("Single transfer test successfully completed!\n"); - // Time the DMA eingine printf("Beginning performance analysis of the DMA engine.\n\n"); rc = time_dma(axidma_dev, tx_channel, tx_buf, tx_size, rx_channel, rx_buf,