Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 338c09a9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull OMAP3 updates from Mauro Carvalho Chehab:
 "Some driver improvements on OMAP3.  This series depend on some iommu
  patches already merged"

* 'topic/omap3isp' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (26 commits)
  [media] omap3isp: Rename isp_buffer isp_addr field to dma
  [media] omap3isp: Move to videobuf2
  [media] v4l: vb2: Add a function to discard all DONE buffers
  [media] omap3isp: Cancel all queued buffers when stopping the video stream
  [media] omap3isp: Move buffer irqlist to isp_buffer structure
  [media] omap3isp: Move queue irqlock to isp_video structure
  [media] omap3isp: Move queue mutex to isp_video structure
  [media] omap3isp: queue: Don't build scatterlist for kernel buffer
  [media] omap3isp: Use the ARM DMA IOMMU-aware operations
  [media] omap3isp: queue: Use sg_alloc_table_from_pages()
  [media] omap3isp: queue: Map PFNMAP buffers to device
  [media] omap3isp: queue: Fix the dma_map_sg() return value check
  [media] omap3isp: queue: Allocate kernel buffers with dma_alloc_coherent
  [media] omap3isp: queue: Inline the ispmmu_v(un)map functions
  [media] omap3isp: queue: Merge the prepare and sglist functions
  [media] omap3isp: queue: Use sg_table structure
  [media] omap3isp: queue: Move IOMMU handling code to the queue
  [media] omap3isp: video: Set the buffer bytesused field at completion time
  [media] omap3isp: ccdc: Use the DMA API for FPC
  [media] omap3isp: ccdc: Use the DMA API for LSC
  ...
parents ed9ea4ed 21d8582d
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -93,7 +93,9 @@ config VIDEO_M32R_AR_M64278

config VIDEO_OMAP3
	tristate "OMAP 3 Camera support"
	depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
	select ARM_DMA_USE_IOMMU
	select OMAP_IOMMU
	---help---
	  Driver for an OMAP 3 camera controller.

+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
ccflags-$(CONFIG_VIDEO_OMAP3_DEBUG) += -DDEBUG

omap3-isp-objs += \
	isp.o ispqueue.o ispvideo.o \
	isp.o ispvideo.o \
	ispcsiphy.o ispccp2.o ispcsi2.o \
	ispccdc.o isppreview.o ispresizer.o \
	ispstat.o isph3a_aewb.o isph3a_af.o isphist.o
+79 −29
Original line number Diff line number Diff line
@@ -69,6 +69,8 @@
#include <linux/sched.h>
#include <linux/vmalloc.h>

#include <asm/dma-iommu.h>

#include <media/v4l2-common.h>
#include <media/v4l2-device.h>

@@ -1397,14 +1399,14 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
	if (isp_pipeline_is_last(me)) {
		struct isp_video *video = pipe->output;
		unsigned long flags;
		spin_lock_irqsave(&video->queue->irqlock, flags);
		spin_lock_irqsave(&video->irqlock, flags);
		if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
			spin_unlock_irqrestore(&video->queue->irqlock, flags);
			spin_unlock_irqrestore(&video->irqlock, flags);
			atomic_set(stopping, 0);
			smp_mb();
			return 0;
		}
		spin_unlock_irqrestore(&video->queue->irqlock, flags);
		spin_unlock_irqrestore(&video->irqlock, flags);
		if (!wait_event_timeout(*wait, !atomic_read(stopping),
					msecs_to_jiffies(1000))) {
			atomic_set(stopping, 0);
@@ -1625,7 +1627,7 @@ struct isp_device *omap3isp_get(struct isp_device *isp)
 * Decrement the reference count on the ISP. If the last reference is released,
 * power-down all submodules, disable clocks and free temporary buffers.
 */
void omap3isp_put(struct isp_device *isp)
static void __omap3isp_put(struct isp_device *isp, bool save_ctx)
{
	if (isp == NULL)
		return;
@@ -1634,7 +1636,7 @@ void omap3isp_put(struct isp_device *isp)
	BUG_ON(isp->ref_count == 0);
	if (--isp->ref_count == 0) {
		isp_disable_interrupts(isp);
		if (isp->domain) {
		if (save_ctx) {
			isp_save_ctx(isp);
			isp->has_context = 1;
		}
@@ -1648,6 +1650,11 @@ void omap3isp_put(struct isp_device *isp)
	mutex_unlock(&isp->isp_mutex);
}

void omap3isp_put(struct isp_device *isp)
{
	__omap3isp_put(isp, true);
}

/* --------------------------------------------------------------------------
 * Platform device driver
 */
@@ -2120,6 +2127,61 @@ static int isp_initialize_modules(struct isp_device *isp)
	return ret;
}

static void isp_detach_iommu(struct isp_device *isp)
{
	arm_iommu_release_mapping(isp->mapping);
	isp->mapping = NULL;
	iommu_group_remove_device(isp->dev);
}

static int isp_attach_iommu(struct isp_device *isp)
{
	struct dma_iommu_mapping *mapping;
	struct iommu_group *group;
	int ret;

	/* Create a device group and add the device to it. */
	group = iommu_group_alloc();
	if (IS_ERR(group)) {
		dev_err(isp->dev, "failed to allocate IOMMU group\n");
		return PTR_ERR(group);
	}

	ret = iommu_group_add_device(group, isp->dev);
	iommu_group_put(group);

	if (ret < 0) {
		dev_err(isp->dev, "failed to add device to IPMMU group\n");
		return ret;
	}

	/*
	 * Create the ARM mapping, used by the ARM DMA mapping core to allocate
	 * VAs. This will allocate a corresponding IOMMU domain.
	 */
	mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
	if (IS_ERR(mapping)) {
		dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
		ret = PTR_ERR(mapping);
		goto error;
	}

	isp->mapping = mapping;

	/* Attach the ARM VA mapping to the device. */
	ret = arm_iommu_attach_device(isp->dev, mapping);
	if (ret < 0) {
		dev_err(isp->dev, "failed to attach device to VA mapping\n");
		goto error;
	}

	return 0;

error:
	isp_detach_iommu(isp);
	return ret;
}

/*
 * isp_remove - Remove ISP platform device
 * @pdev: Pointer to ISP platform device
@@ -2135,10 +2197,8 @@ static int isp_remove(struct platform_device *pdev)
	isp_xclk_cleanup(isp);

	__omap3isp_get(isp, false);
	iommu_detach_device(isp->domain, &pdev->dev);
	iommu_domain_free(isp->domain);
	isp->domain = NULL;
	omap3isp_put(isp);
	isp_detach_iommu(isp);
	__omap3isp_put(isp, false);

	return 0;
}
@@ -2265,39 +2325,32 @@ static int isp_probe(struct platform_device *pdev)
		}
	}

	isp->domain = iommu_domain_alloc(pdev->dev.bus);
	if (!isp->domain) {
		dev_err(isp->dev, "can't alloc iommu domain\n");
		ret = -ENOMEM;
	/* IOMMU */
	ret = isp_attach_iommu(isp);
	if (ret < 0) {
		dev_err(&pdev->dev, "unable to attach to IOMMU\n");
		goto error_isp;
	}

	ret = iommu_attach_device(isp->domain, &pdev->dev);
	if (ret) {
		dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
		ret = -EPROBE_DEFER;
		goto free_domain;
	}

	/* Interrupt */
	isp->irq_num = platform_get_irq(pdev, 0);
	if (isp->irq_num <= 0) {
		dev_err(isp->dev, "No IRQ resource\n");
		ret = -ENODEV;
		goto detach_dev;
		goto error_iommu;
	}

	if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED,
			     "OMAP3 ISP", isp)) {
		dev_err(isp->dev, "Unable to request IRQ\n");
		ret = -EINVAL;
		goto detach_dev;
		goto error_iommu;
	}

	/* Entities */
	ret = isp_initialize_modules(isp);
	if (ret < 0)
		goto detach_dev;
		goto error_iommu;

	ret = isp_register_entities(isp);
	if (ret < 0)
@@ -2310,14 +2363,11 @@ static int isp_probe(struct platform_device *pdev)

error_modules:
	isp_cleanup_modules(isp);
detach_dev:
	iommu_detach_device(isp->domain, &pdev->dev);
free_domain:
	iommu_domain_free(isp->domain);
	isp->domain = NULL;
error_iommu:
	isp_detach_iommu(isp);
error_isp:
	isp_xclk_cleanup(isp);
	omap3isp_put(isp);
	__omap3isp_put(isp, false);
error:
	mutex_destroy(&isp->isp_mutex);

+3 −5
Original line number Diff line number Diff line
@@ -45,8 +45,6 @@
#include "ispcsi2.h"
#include "ispccp2.h"

#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)

#define ISP_TOK_TERM		0xFFFFFFFF	/*
						 * terminating token for ISP
						 * modules reg list
@@ -152,6 +150,7 @@ struct isp_xclk {
 *             regions.
 * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
 *                  regions.
 * @mapping: IOMMU mapping
 * @stat_lock: Spinlock for handling statistics
 * @isp_mutex: Mutex for serializing requests to ISP.
 * @stop_failure: Indicates that an entity failed to stop.
@@ -171,7 +170,6 @@ struct isp_xclk {
 * @isp_res: Pointer to current settings for ISP Resizer.
 * @isp_prev: Pointer to current settings for ISP Preview.
 * @isp_ccdc: Pointer to current settings for ISP CCDC.
 * @iommu: Pointer to requested IOMMU instance for ISP.
 * @platform_cb: ISP driver callback function pointers for platform code
 *
 * This structure is used to store the OMAP ISP Information.
@@ -189,6 +187,8 @@ struct isp_device {
	void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
	unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];

	struct dma_iommu_mapping *mapping;

	/* ISP Obj */
	spinlock_t stat_lock;	/* common lock for statistic drivers */
	struct mutex isp_mutex;	/* For handling ref_count field */
@@ -219,8 +219,6 @@ struct isp_device {

	unsigned int sbl_resources;
	unsigned int subclk_resources;

	struct iommu_domain *domain;
};

#define v4l2_dev_to_isp_device(dev) \
+53 −54
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/omap-iommu.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <media/v4l2-event.h>
@@ -206,7 +205,8 @@ static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
 * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
 * @ccdc: Pointer to ISP CCDC device.
 */
static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc,
				   dma_addr_t addr)
{
	isp_reg_writel(to_isp_device(ccdc), addr,
		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
@@ -333,7 +333,7 @@ static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
		return -EBUSY;

	ccdc_lsc_setup_regs(ccdc, &req->config);
	ccdc_lsc_program_table(ccdc, req->table);
	ccdc_lsc_program_table(ccdc, req->table.dma);
	return 0;
}

@@ -368,11 +368,12 @@ static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
	if (req == NULL)
		return;

	if (req->iovm)
		dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
			     req->iovm->sgt->nents, DMA_TO_DEVICE);
	if (req->table)
		omap_iommu_vfree(isp->domain, isp->dev, req->table);
	if (req->table.addr) {
		sg_free_table(&req->table.sgt);
		dma_free_coherent(isp->dev, req->config.size, req->table.addr,
				  req->table.dma);
	}

	kfree(req);
}

@@ -416,7 +417,6 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
	struct isp_device *isp = to_isp_device(ccdc);
	struct ispccdc_lsc_config_req *req;
	unsigned long flags;
	void *table;
	u16 update;
	int ret;

@@ -444,38 +444,31 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,

		req->enable = 1;

		req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
					req->config.size, IOMMU_FLAG);
		if (IS_ERR_VALUE(req->table)) {
			req->table = 0;
			ret = -ENOMEM;
			goto done;
		}

		req->iovm = omap_find_iovm_area(isp->dev, req->table);
		if (req->iovm == NULL) {
		req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
						     &req->table.dma,
						     GFP_KERNEL);
		if (req->table.addr == NULL) {
			ret = -ENOMEM;
			goto done;
		}

		if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
				req->iovm->sgt->nents, DMA_TO_DEVICE)) {
			ret = -ENOMEM;
			req->iovm = NULL;
		ret = dma_get_sgtable(isp->dev, &req->table.sgt,
				      req->table.addr, req->table.dma,
				      req->config.size);
		if (ret < 0)
			goto done;
		}

		dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
				    req->iovm->sgt->nents, DMA_TO_DEVICE);
		dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
				    req->table.sgt.nents, DMA_TO_DEVICE);

		table = omap_da_to_va(isp->dev, req->table);
		if (copy_from_user(table, config->lsc, req->config.size)) {
		if (copy_from_user(req->table.addr, config->lsc,
				   req->config.size)) {
			ret = -EFAULT;
			goto done;
		}

		dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
				       req->iovm->sgt->nents, DMA_TO_DEVICE);
		dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
				       req->table.sgt.nents, DMA_TO_DEVICE);
	}

	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
@@ -584,7 +577,7 @@ static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
	if (!ccdc->fpc_en)
		return;

	isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
	isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
		       ISPCCDC_FPC_ADDR);
	/* The FPNUM field must be set before enabling FPC. */
	isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
@@ -724,8 +717,9 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
	ccdc->shadow_update = 0;

	if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
		u32 table_old = 0;
		u32 table_new;
		struct omap3isp_ccdc_fpc fpc;
		struct ispccdc_fpc fpc_old = { .addr = NULL, };
		struct ispccdc_fpc fpc_new;
		u32 size;

		if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
@@ -734,35 +728,39 @@ static int ccdc_config(struct isp_ccdc_device *ccdc,
		ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);

		if (ccdc->fpc_en) {
			if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
					   sizeof(ccdc->fpc)))
			if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc)))
				return -EFAULT;

			size = fpc.fpnum * 4;

			/*
			 * table_new must be 64-bytes aligned, but it's
			 * already done by omap_iommu_vmalloc().
			 */
			size = ccdc->fpc.fpnum * 4;
			table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
							0, size, IOMMU_FLAG);
			if (IS_ERR_VALUE(table_new))
			 * The table address must be 64-bytes aligned, which is
			 * guaranteed by dma_alloc_coherent().
			 */
			fpc_new.fpnum = fpc.fpnum;
			fpc_new.addr = dma_alloc_coherent(isp->dev, size,
							  &fpc_new.dma,
							  GFP_KERNEL);
			if (fpc_new.addr == NULL)
				return -ENOMEM;

			if (copy_from_user(omap_da_to_va(isp->dev, table_new),
					   (__force void __user *)
					   ccdc->fpc.fpcaddr, size)) {
				omap_iommu_vfree(isp->domain, isp->dev,
								table_new);
			if (copy_from_user(fpc_new.addr,
					   (__force void __user *)fpc.fpcaddr,
					   size)) {
				dma_free_coherent(isp->dev, size, fpc_new.addr,
						  fpc_new.dma);
				return -EFAULT;
			}

			table_old = ccdc->fpc.fpcaddr;
			ccdc->fpc.fpcaddr = table_new;
			fpc_old = ccdc->fpc;
			ccdc->fpc = fpc_new;
		}

		ccdc_configure_fpc(ccdc);
		if (table_old != 0)
			omap_iommu_vfree(isp->domain, isp->dev, table_old);

		if (fpc_old.addr != NULL)
			dma_free_coherent(isp->dev, fpc_old.fpnum * 4,
					  fpc_old.addr, fpc_old.dma);
	}

	return ccdc_lsc_config(ccdc, ccdc_struct);
@@ -1523,7 +1521,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)

	buffer = omap3isp_video_buffer_next(&ccdc->video_out);
	if (buffer != NULL) {
		ccdc_set_outaddr(ccdc, buffer->isp_addr);
		ccdc_set_outaddr(ccdc, buffer->dma);
		restart = 1;
	}

@@ -1662,7 +1660,7 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
	if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
		return -ENODEV;

	ccdc_set_outaddr(ccdc, buffer->isp_addr);
	ccdc_set_outaddr(ccdc, buffer->dma);

	/* We now have a buffer queued on the output, restart the pipeline
	 * on the next CCDC interrupt if running in continuous mode (or when
@@ -2580,8 +2578,9 @@ void omap3isp_ccdc_cleanup(struct isp_device *isp)
	cancel_work_sync(&ccdc->lsc.table_work);
	ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);

	if (ccdc->fpc.fpcaddr != 0)
		omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
	if (ccdc->fpc.addr != NULL)
		dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr,
				  ccdc->fpc.dma);

	mutex_destroy(&ccdc->ioctl_lock);
}
Loading