Skip to content

Commit

Permalink
Added support for frame-based VDMA receive transfers.
Browse files Browse the repository at this point in the history
This was implemented as discussed in issue #31. The software stack now
supports receive video transfers. This is a continuous receive transfer
through VDMA, or a loop transfer. Transfers like this are useful for
cameras or other video input devices that utilize frames. The call
supports an arbitrary number of frame buffers.

NOTE: This code is untested, and was only checked for correct
compilation.
  • Loading branch information
bperez77 committed Dec 1, 2017
1 parent 16732e7 commit c3181d8
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 23 deletions.
5 changes: 3 additions & 2 deletions driver/axidma.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ int axidma_write_transfer(struct axidma_device *dev,
struct axidma_transaction *trans);
int axidma_rw_transfer(struct axidma_device *dev,
struct axidma_inout_transaction *trans);
int axidma_video_write_transfer(struct axidma_device *dev,
struct axidma_video_transaction *trans);
int axidma_video_transfer(struct axidma_device *dev,
struct axidma_video_transaction *trans,
enum axidma_dir dir);
int axidma_stop_channel(struct axidma_device *dev, struct axidma_chan *chan);
dma_addr_t axidma_uservirt_to_dma(struct axidma_device *dev, void *user_addr,
size_t size);
Expand Down
23 changes: 22 additions & 1 deletion driver/axidma_chrdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,26 @@ static long axidma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
rc = axidma_rw_transfer(dev, &inout_trans);
break;

case AXIDMA_DMA_VIDEO_READ:
if (copy_from_user(&video_trans, arg_ptr,
sizeof(video_trans)) != 0) {
axidma_err("Unable to copy transfer info from userspace for "
"AXIDMA_DMA_VIDEO_READ.\n");
return -EFAULT;
}

// Verify that we can access the array of frame buffers
size = video_trans.num_frame_buffers *
sizeof(video_trans.frame_buffers[0]);
if (!axidma_access_ok(video_trans.frame_buffers, size, true)) {
axidma_err("Unable to copy frame buffer addresses from "
"userspace for AXIDMA_DMA_VIDEO_WRITE.\n");
return -EFAULT;
}

rc = axidma_video_transfer(dev, &video_trans, AXIDMA_READ);
break;

case AXIDMA_DMA_VIDEO_WRITE:
if (copy_from_user(&video_trans, arg_ptr,
sizeof(video_trans)) != 0) {
Expand All @@ -454,6 +474,7 @@ static long axidma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}

// Verify that we can access the array of frame buffers
// TODO: This array should be copied into kernel space
size = video_trans.num_frame_buffers *
sizeof(video_trans.frame_buffers[0]);
if (!axidma_access_ok(video_trans.frame_buffers, size, true)) {
Expand All @@ -462,7 +483,7 @@ static long axidma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EFAULT;
}

rc = axidma_video_write_transfer(dev, &video_trans);
rc = axidma_video_transfer(dev, &video_trans, AXIDMA_WRITE);
break;

case AXIDMA_STOP_DMA_CHANNEL:
Expand Down
7 changes: 4 additions & 3 deletions driver/axidma_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,9 @@ int axidma_rw_transfer(struct axidma_device *dev,
return 0;
}

int axidma_video_write_transfer(struct axidma_device *dev,
struct axidma_video_transaction *trans)
int axidma_video_transfer(struct axidma_device *dev,
struct axidma_video_transaction *trans,
enum axidma_dir dir)
{
int rc, i;
size_t image_size;
Expand All @@ -550,7 +551,7 @@ int axidma_video_write_transfer(struct axidma_device *dev,
// Setup transmit transfer structure for DMA
struct axidma_transfer tx_tfr = {
.sg_len = trans->num_frame_buffers,
.dir = AXIDMA_WRITE,
.dir = dir,
.type = AXIDMA_VDMA,
.wait = false,
.channel_id = trans->channel_id,
Expand Down
34 changes: 31 additions & 3 deletions include/axidma_ioctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,34 @@ struct axidma_video_transaction {
#define AXIDMA_DMA_READWRITE _IOR(AXIDMA_IOCTL_MAGIC, 6, \
struct axidma_inout_transaction)

/**
* Performs frame-buffer based transfers from a camera on the fabric.
*
* This function performs a video transfer from the logic fabric. It receives
* the given buffers from logic fabric (intended for a camera pipeline). When it
* reaches the end of the buffers, it loops back and receives the data again in
* the first buffer. This is used for frame-buffer based cameras.
*
* All of the frame buffers must be within an address range that was allocated
* by a call to mmap with the AXI DMA device. Also, each buffer must
* be able to hold a frame of (width * height * depth) bytes. The input array of
* buffers must be a memory location that holds `num_frame_buffers` addresses.
*
* This call is always non-blocking as the VDMA engine will run forever. In
* order to end the transaction, you must make a call to the stop dma channel
* ioctl.
*
* Inputs:
* - channel_id - The id for the channel you want to send data over.
* - num_frame_buffers - The number of frame buffers you're using.
* - frame_buffers - An array of the frame buffer addresses.
* - width - The width of the frame (image) in pixels.
* - height - The height of the frame in lines.
* - depth - The size of each pixel in the frame in bytes.
**/
#define AXIDMA_DMA_VIDEO_READ _IOR(AXIDMA_IOCTL_MAGIC, 7, \
struct axidma_video_transaction)

/**
* Performs frame-buffer based transfers to a display on the logic fabric.
*
Expand All @@ -299,7 +327,7 @@ struct axidma_video_transaction {
* - height - The height of the frame in lines.
* - depth - The size of each pixel in the frame in bytes.
**/
#define AXIDMA_DMA_VIDEO_WRITE _IOR(AXIDMA_IOCTL_MAGIC, 7, \
#define AXIDMA_DMA_VIDEO_WRITE _IOR(AXIDMA_IOCTL_MAGIC, 8, \
struct axidma_video_transaction)

/**
Expand All @@ -315,7 +343,7 @@ struct axidma_video_transaction {
* - channel_id - The integer id for the channel.
* - chan - This field is unused an can be safely left uninitialized.
*/
#define AXIDMA_STOP_DMA_CHANNEL _IOR(AXIDMA_IOCTL_MAGIC, 8, \
#define AXIDMA_STOP_DMA_CHANNEL _IOR(AXIDMA_IOCTL_MAGIC, 9, \
struct axidma_chan)

/**
Expand All @@ -330,6 +358,6 @@ struct axidma_video_transaction {
* Inputs:
* - user_addr - The user virtual address of the external DMA buffer.
**/
#define AXIDMA_UNREGISTER_BUFFER _IO(AXIDMA_IOCTL_MAGIC, 9)
#define AXIDMA_UNREGISTER_BUFFER _IO(AXIDMA_IOCTL_MAGIC, 10)

#endif /* AXIDMA_IOCTL_H_ */
17 changes: 10 additions & 7 deletions include/libaxidma.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,16 +242,19 @@ int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
size_t tx_len, int rx_channel, void *rx_buf, size_t rx_len, bool wait);

/**
* Starts a video DMA (VDMA) transfer on the given DMA channel.
* Starts a video DMA (VDMA) loop/continuous transfer on the given channel.
*
* A video DMA transfer differs from a typical DMA transfer in that it is
* cyclic, and ends only when requested by the user. A VDMA transfer will
* continuously transmit the frame buffers, transmitting the first buffer,
* then the second, etc., and then repeating once the last buffer is reached.
* A video loop transfer differs from a typical DMA transfer in that it is
* cyclic, and ends only when requested by the user. A video loop transfer will
* continuously transmit/receive the frame buffers, transmitting the first
* buffer, then the second, etc., and then repeating from the beginning once the
* last buffer is reached. This is suitable when continuously sending data to a
* display, or continuous receiving data from a camera.
*
* This function supports an arbitrary number of frame buffers, allowing
* for both double-buffering and triple-buffering. This will only transmit
* the buffers, the receive direction is not supported.
* for both double-buffering and triple-buffering. This function is
* non-blocking, and returns immediately. The only way to stop the transfer is
* via a call to #axidma_stop_transfer.
*
* @param[in] dev An #axidma_dev_t returned by #axidma_init.
* @param[in] display_channel DMA channel the video transfer will take place
Expand Down
18 changes: 11 additions & 7 deletions library/libaxidma.c
Original file line number Diff line number Diff line change
Expand Up @@ -499,29 +499,33 @@ int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
return rc;
}

/* This function performs a video transfer over AXI DMA, setting up the DMA to
* read from the given frame buffers on-demand continuously. This call is
* always non-blocking. The transfer must be stopped with a call to
* axidma_stop_transfer. */
/* This function performs a video transfer over AXI DMA, setting up a VDMA
* channel to either read from or write to given frame buffers on-demand
* continuously. This call is always non-blocking. The transfer can only be
* stopped with a call to axidma_stop_transfer. */
int axidma_video_transfer(axidma_dev_t dev, int display_channel, size_t width,
size_t height, size_t depth, void **frame_buffers, int num_buffers)
{
int rc;
unsigned long axidma_cmd;
struct axidma_video_transaction trans;
dma_channel_t *dma_chan;

assert(find_channel(dev, display_channel) != NULL);
assert(find_channel(dev, display_channel)->dir == AXIDMA_WRITE);
assert(find_channel(dev, display_channel)->type == AXIDMA_VDMA);

// Setup the argument structure for the IOCTL
dma_chan = find_channel(dev, display_channel);
trans.channel_id = display_channel;
trans.num_frame_buffers = num_buffers;
trans.frame_buffers = frame_buffers;
trans.width = width;
trans.height = height;
trans.depth = depth;

axidma_cmd = (dma_chan->dir == AXIDMA_READ) ? AXIDMA_DMA_VIDEO_READ :
AXIDMA_DMA_VIDEO_WRITE;
// Perform the video transfer
rc = ioctl(dev->fd, AXIDMA_DMA_VIDEO_WRITE, &trans);
rc = ioctl(dev->fd, axidma_cmd, &trans);
if (rc < 0) {
perror("Failed to perform the AXI DMA video write transfer");
}
Expand Down

0 comments on commit c3181d8

Please sign in to comment.