From patchwork Wed Jan 4 13:34:41 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Noralf_Tr=C3=B8nnes?= X-Patchwork-Id: 9496953 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1239660237 for ; Wed, 4 Jan 2017 14:10:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 02EB128068 for ; Wed, 4 Jan 2017 14:10:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EB5802808F; Wed, 4 Jan 2017 14:10:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 59B0C28068 for ; Wed, 4 Jan 2017 14:10:07 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C97666E258; Wed, 4 Jan 2017 14:09:58 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from smtp.domeneshop.no (smtp.domeneshop.no [IPv6:2a01:5b40:0:3005::1]) by gabe.freedesktop.org (Postfix) with ESMTPS id D0ACA6E258 for ; Wed, 4 Jan 2017 14:09:56 +0000 (UTC) Received: from 211.81-166-168.customer.lyse.net ([81.166.168.211]:42458 helo=localhost.localdomain) by smtp.domeneshop.no with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1cOlij-00006b-W0; Wed, 04 Jan 2017 14:35:14 +0100 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= To: dri-devel@lists.freedesktop.org Subject: [RFC 5/6] spi: spidev: Add dma-buf support Date: Wed, 4 Jan 2017 14:34:41 +0100 Message-Id: <20170104133442.4534-6-noralf@tronnes.org> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20170104133442.4534-1-noralf@tronnes.org> References: <20170104133442.4534-1-noralf@tronnes.org> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add support for using dma-buf buffers in transfers from userspace. FIXME: Backwards compatibility needs to be taken care of somehow. Signed-off-by: Noralf Trønnes --- drivers/spi/Kconfig | 1 + drivers/spi/spidev.c | 158 +++++++++++++++++++++++++++++++++++++++- include/uapi/linux/spi/spidev.h | 8 ++ 3 files changed, 163 insertions(+), 4 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b799547..ac6bbd1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -731,6 +731,7 @@ comment "SPI Protocol Masters" config SPI_SPIDEV tristate "User mode SPI device driver support" + select SG_SPLIT if DMA_SHARED_BUFFER help This supports user mode SPI protocol drivers. diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index d780491..35e6377 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,6 +89,16 @@ struct spidev_data { u32 speed_hz; }; +struct spidev_dmabuf { + struct device *dev; + struct dma_buf_attachment *attach; + enum dma_data_direction dir; + struct sg_table *sgt_dmabuf; + void *vaddr; + struct scatterlist *sgl; + unsigned int nents; +}; + static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); @@ -205,21 +217,122 @@ spidev_write(struct file *filp, const char __user *buf, return status; } +#ifdef CONFIG_DMA_SHARED_BUFFER + +static int spidev_dmabuf_map(struct spidev_dmabuf *sdmabuf, struct device *dev, + int fd, enum dma_data_direction dir, + u32 offset, u32 len) +{ + struct dma_buf_attachment *attach; + struct dma_buf *dmabuf; + struct sg_table *sgt; + void *vaddr; + size_t sizes[1] = { len, }; + struct scatterlist *out[1]; + int out_nents[1]; + int ret; + + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + attach = dma_buf_attach(dmabuf, dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto err_put; + } + + sgt = dma_buf_map_attachment(attach, dir); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_detach; + } + + ret = sg_split(sgt->sgl, sgt->nents, offset, 1, sizes, out, out_nents, GFP_KERNEL); + if (ret) { + goto err_unmap; + } + + /* A virtual address is only necessary if master can't do dma. */ +// ret = dma_buf_begin_cpu_access(dmabuf, dir); +// if (ret) +// goto err_free_sg; + + vaddr = dma_buf_vmap(dmabuf); + if (!vaddr) { + ret = -ENOMEM; + goto err_end_access; + } + + sdmabuf->dev = dev; + sdmabuf->attach = attach; + sdmabuf->dir = dir; + sdmabuf->sgt_dmabuf = sgt; + sdmabuf->vaddr = vaddr; + sdmabuf->sgl = out[0]; + sdmabuf->nents = out_nents[0]; + + return 0; + +err_end_access: +// dma_buf_end_cpu_access(dmabuf, dir); +//err_free_sg: + kfree(out[0]); +err_unmap: + dma_buf_unmap_attachment(attach, sgt, dir); +err_detach: + dma_buf_detach(dmabuf, attach); +err_put: + dma_buf_put(dmabuf); + + return ret; +} + +static void spidev_dmabuf_unmap(struct spidev_dmabuf *sdmabuf) +{ + struct dma_buf *dmabuf; + + if (!sdmabuf->attach) + return; + + dmabuf = sdmabuf->attach->dmabuf; + dma_buf_vunmap(dmabuf, sdmabuf->vaddr); +// dma_buf_end_cpu_access(dmabuf, sdmabuf->dir); + dma_buf_unmap_attachment(sdmabuf->attach, sdmabuf->sgt_dmabuf, sdmabuf->dir); + dma_buf_detach(dmabuf, sdmabuf->attach); + dma_buf_put(dmabuf); +} +#else +static int spidev_dmabuf_map(struct spidev_dmabuf *sdmabuf, struct device *dev, + int fd, enum dma_data_direction dir, + u32 offset, u32 len) +{ + return -ENOTSUPP; +} + +static void spidev_dmabuf_unmap(struct spidev_dmabuf *sdmabuf) {} +#endif + static int spidev_message(struct spidev_data *spidev, struct spi_ioc_transfer *u_xfers, unsigned n_xfers) { + struct spi_device *spi = spidev->spi; struct spi_message msg; struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; + struct spidev_dmabuf *sdmabufs, *s_tmp; unsigned n, total, tx_total, rx_total; u8 *tx_buf, *rx_buf; int status = -EFAULT; spi_message_init(&msg); k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); - if (k_xfers == NULL) - return -ENOMEM; + sdmabufs = kcalloc(n_xfers * 2, sizeof(*s_tmp), GFP_KERNEL); + if (!k_xfers || !sdmabufs) { + status = -ENOMEM; + goto done; + } /* Construct spi_message, copying any tx data to bounce buffer. * We walk the array of user-provided transfers, using each one @@ -230,6 +343,7 @@ static int spidev_message(struct spidev_data *spidev, total = 0; tx_total = 0; rx_total = 0; + s_tmp = sdmabufs; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; n--, k_tmp++, u_tmp++) { @@ -259,7 +373,12 @@ static int spidev_message(struct spidev_data *spidev, u_tmp->len)) goto done; rx_buf += k_tmp->len; + } else if (u_tmp->rx_dma_fd > 0) { + /* TODO */ + status = -ENOTSUPP; + goto done; } + if (u_tmp->tx_buf) { /* this transfer needs space in TX bounce buffer */ tx_total += k_tmp->len; @@ -273,6 +392,31 @@ static int spidev_message(struct spidev_data *spidev, u_tmp->len)) goto done; tx_buf += k_tmp->len; + } else if (u_tmp->tx_dma_fd > 0) { + struct device *tx_dev; + + if (k_tmp->len > spi->master->max_dma_len) { + status = -EMSGSIZE; + goto done; + } + + if (spi->master->dma_tx) + tx_dev = spi->master->dma_tx->device->dev; + else + tx_dev = &spi->master->dev; + + status = spidev_dmabuf_map(s_tmp, tx_dev, + u_tmp->tx_dma_fd, + DMA_TO_DEVICE, + u_tmp->dma_offset, + k_tmp->len); + if (status) + goto done; + + k_tmp->tx_sg.sgl = s_tmp->sgl; + k_tmp->tx_sg.nents = s_tmp->nents; + k_tmp->tx_buf = s_tmp->vaddr + u_tmp->dma_offset; + s_tmp++; } k_tmp->cs_change = !!u_tmp->cs_change; @@ -287,8 +431,10 @@ static int spidev_message(struct spidev_data *spidev, dev_dbg(&spidev->spi->dev, " xfer len %u %s%s%s%dbits %u usec %uHz\n", u_tmp->len, - u_tmp->rx_buf ? "rx " : "", - u_tmp->tx_buf ? "tx " : "", + u_tmp->rx_buf ? "rx " : + u_tmp->rx_dma_fd ? "tx-dma" : "", + u_tmp->tx_buf ? "tx " : + u_tmp->tx_dma_fd ? "rx-dma" : "", u_tmp->cs_change ? "cs " : "", u_tmp->bits_per_word ? : spidev->spi->bits_per_word, u_tmp->delay_usecs, @@ -317,6 +463,10 @@ static int spidev_message(struct spidev_data *spidev, status = total; done: + for (n = n_xfers, s_tmp = sdmabufs; n; n--, s_tmp++) + spidev_dmabuf_unmap(s_tmp); + + kfree(sdmabufs); kfree(k_xfers); return status; } diff --git a/include/uapi/linux/spi/spidev.h b/include/uapi/linux/spi/spidev.h index dd5f21e..a9b6cd0 100644 --- a/include/uapi/linux/spi/spidev.h +++ b/include/uapi/linux/spi/spidev.h @@ -64,6 +64,9 @@ * @delay_usecs: If nonzero, how long to delay after the last bit transfer * before optionally deselecting the device before the next transfer. * @cs_change: True to deselect device before starting the next transfer. + * @tx_dma_fd: File descriptor for transmitting dma-buf buffers. + * @rx_dma_fd: File descriptor for receiving dma-buf buffers. + * @dma_offset: Offset into dma-buf buffer. * * This structure is mapped directly to the kernel spi_transfer structure; * the fields have the same meanings, except of course that the pointers @@ -100,6 +103,11 @@ struct spi_ioc_transfer { __u8 rx_nbits; __u16 pad; + __s32 tx_dma_fd; + __s32 rx_dma_fd; + __u32 dma_offset; + __u32 pad2; + /* If the contents of 'struct spi_ioc_transfer' ever change * incompatibly, then the ioctl number (currently 0) must change; * ioctls with constant size fields get a bit more in the way of