/* * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2 * * Copyright (C) 2010 Samsung Electronics * * Author: Pawel Osciak <pawel@osciak.com> * * 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.
*/
/* * This function may fail if: * * - dma_buf_vmap() fails * E.g. due to lack of virtual mapping address space, or due to * dmabuf->ops misconfiguration. * * - dma_vmap_noncontiguous() fails * For instance, when requested buffer size is larger than totalram_pages(). * Relevant for buffers that use non-coherent memory. * * - Queue DMA attrs have DMA_ATTR_NO_KERNEL_MAPPING set * Relevant for buffers that use coherent memory.
*/ staticvoid *vb2_dc_vaddr(struct vb2_buffer *vb, void *buf_priv)
{ struct vb2_dc_buf *buf = buf_priv;
if (buf->vaddr) return buf->vaddr;
if (buf->db_attach) { struct iosys_map map;
if (!dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map))
buf->vaddr = map.vaddr;
return buf->vaddr;
}
if (buf->non_coherent_mem)
buf->vaddr = dma_vmap_noncontiguous(buf->dev, buf->size,
buf->dma_sgt); return buf->vaddr;
}
attach = kzalloc(sizeof(*attach), GFP_KERNEL); if (!attach) return -ENOMEM;
sgt = &attach->sgt; /* Copy the buf->base_sgt scatter list to the attachment, as we can't * map the same scatter list to multiple attachments at the same time.
*/
ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL); if (ret) {
kfree(attach); return -ENOMEM;
}
rd = buf->sgt_base->sgl;
wr = sgt->sgl; for (i = 0; i < sgt->orig_nents; ++i) {
sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
rd = sg_next(rd);
wr = sg_next(wr);
}
/* release the scatterlist cache */ if (attach->dma_dir != DMA_NONE) /* * Cache sync can be skipped here, as the vb2_dc memory is * allocated from device coherent memory, which means the * memory locations do not require any explicit cache * maintenance prior or after being used by the device.
*/
dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir,
DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sgt);
kfree(attach);
db_attach->priv = NULL;
}
/* release any previous cache */ if (attach->dma_dir != DMA_NONE) {
dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir,
DMA_ATTR_SKIP_CPU_SYNC);
attach->dma_dir = DMA_NONE;
}
/* * mapping to the client with new direction, no cache sync * required see comment in vb2_dc_dmabuf_ops_detach()
*/ if (dma_map_sgtable(db_attach->dev, sgt, dma_dir,
DMA_ATTR_SKIP_CPU_SYNC)) {
pr_err("failed to map scatterlist\n"); return ERR_PTR(-EIO);
}
attach->dma_dir = dma_dir;
return sgt;
}
staticvoid vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach, struct sg_table *sgt, enum dma_data_direction dma_dir)
{ /* nothing to be done here */
}
staticvoid vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf)
{ /* drop reference obtained in vb2_dc_get_dmabuf */
vb2_dc_put(dbuf->priv);
}
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) {
dev_err(buf->dev, "failed to alloc sg table\n"); return NULL;
}
ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr,
buf->size, buf->attrs); if (ret < 0) {
dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
kfree(sgt); return NULL;
}
if (sgt) { /* * No need to sync to CPU, it's already synced to the CPU * since the finish() memop will have been called before this.
*/
dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir,
DMA_ATTR_SKIP_CPU_SYNC); if (buf->dma_dir == DMA_FROM_DEVICE ||
buf->dma_dir == DMA_BIDIRECTIONAL) {
pages = frame_vector_pages(buf->vec); /* sgt should exist only if vector contains pages... */ if (!WARN_ON_ONCE(IS_ERR(pages))) for (i = 0; i < frame_vector_count(buf->vec); i++)
set_page_dirty_lock(pages[i]);
}
sg_free_table(sgt);
kfree(sgt);
} else {
dma_unmap_resource(buf->dev, buf->dma_addr, buf->size,
buf->dma_dir, 0);
}
vb2_destroy_framevec(buf->vec);
kfree(buf);
}
/* Only cache aligned DMA transfers are reliable */ if (!IS_ALIGNED(vaddr | size, dma_align)) {
pr_debug("user data must be aligned to %lu bytes\n", dma_align); return ERR_PTR(-EINVAL);
}
if (!size) {
pr_debug("size is zero\n"); return ERR_PTR(-EINVAL);
}
if (WARN_ON(!dev)) return ERR_PTR(-EINVAL);
buf = kzalloc(sizeof *buf, GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM);
/* * Failed to convert to pages... Check the memory is physically * contiguous and use direct mapping
*/ for (i = 1; i < n_pages; i++) if (nums[i-1] + 1 != nums[i]) goto fail_pfnvec;
buf->dma_addr = dma_map_resource(buf->dev,
__pfn_to_phys(nums[0]), size, buf->dma_dir, 0); if (dma_mapping_error(buf->dev, buf->dma_addr)) {
ret = -ENOMEM; goto fail_pfnvec;
} goto out;
}
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); if (!sgt) {
pr_err("failed to allocate sg table\n");
ret = -ENOMEM; goto fail_pfnvec;
}
ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages,
offset, size, GFP_KERNEL); if (ret) {
pr_err("failed to initialize sg table\n"); goto fail_sgt;
}
/* * No need to sync to the device, this will happen later when the * prepare() memop is called.
*/ if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir,
DMA_ATTR_SKIP_CPU_SYNC)) {
pr_err("failed to map scatterlist\n");
ret = -EIO; goto fail_sgt_init;
}
contig_size = vb2_dc_get_contiguous_size(sgt); if (contig_size < size) {
pr_err("contiguous mapping is too small %lu/%lu\n",
contig_size, size);
ret = -EFAULT; goto fail_map_sg;
}
if (WARN_ON(!buf->db_attach)) {
pr_err("trying to pin a non attached buffer\n"); return -EINVAL;
}
if (WARN_ON(buf->dma_sgt)) {
pr_err("dmabuf buffer is already pinned\n"); return 0;
}
/* get the associated scatterlist for this buffer */
sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir); if (IS_ERR(sgt)) {
pr_err("Error getting dmabuf scatterlist\n"); return -EINVAL;
}
/* checking if dmabuf is big enough to store contiguous chunk */
contig_size = vb2_dc_get_contiguous_size(sgt); if (contig_size < buf->size) {
pr_err("contiguous chunk is too small %lu/%lu\n",
contig_size, buf->size);
dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt,
buf->dma_dir); return -EFAULT;
}
buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM);
buf->dev = dev;
buf->vb = vb;
/* create attachment for the dmabuf with the user device */
dba = dma_buf_attach(dbuf, buf->dev); if (IS_ERR(dba)) {
pr_err("failed to attach dmabuf\n");
kfree(buf); return dba;
}
/** * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size * @dev: device for configuring DMA parameters * @size: size of DMA max segment size to set * * To allow mapping the scatter-list into a single chunk in the DMA * address space, the device is required to have the DMA max segment * size parameter set to a value larger than the buffer size. Otherwise, * the DMA-mapping subsystem will split the mapping into max segment * size chunks. This function sets the DMA max segment size * parameter to let DMA-mapping map a buffer as a single chunk in DMA * address space. * This code assumes that the DMA-mapping subsystem will merge all * scatterlist segments if this is really possible (for example when * an IOMMU is available and enabled). * Ideally, this parameter should be set by the generic bus code, but it * is left with the default 64KiB value due to historical litmiations in * other subsystems (like limited USB host drivers) and there no good * place to set it to the proper value. * This function should be called from the drivers, which are known to * operate on platforms with IOMMU and provide access to shared buffers * (either USERPTR or DMABUF). This should be done before initializing * videobuf2 queue.
*/ int vb2_dma_contig_set_max_seg_size(struct device *dev, unsignedint size)
{ if (!dev->dma_parms) {
dev_err(dev, "Failed to set max_seg_size: dma_parms is NULL\n"); return -ENODEV;
} if (dma_get_max_seg_size(dev) < size)
dma_set_max_seg_size(dev, size); return 0;
}
EXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.