diff options
Diffstat (limited to 'drivers/media/platform/omap3isp')
19 files changed, 613 insertions, 1886 deletions
diff --git a/drivers/media/platform/omap3isp/Makefile b/drivers/media/platform/omap3isp/Makefile index e8847e79e31..254975a9174 100644 --- a/drivers/media/platform/omap3isp/Makefile +++ b/drivers/media/platform/omap3isp/Makefile @@ -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 diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index df3a0ec7fd2..2c7aa672056 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -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> @@ -290,9 +292,11 @@ static int isp_xclk_init(struct isp_device *isp)  	struct clk_init_data init;  	unsigned int i; +	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) +		isp->xclks[i].clk = ERR_PTR(-EINVAL); +  	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {  		struct isp_xclk *xclk = &isp->xclks[i]; -		struct clk *clk;  		xclk->isp = isp;  		xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; @@ -305,10 +309,15 @@ static int isp_xclk_init(struct isp_device *isp)  		init.num_parents = 1;  		xclk->hw.init = &init; - -		clk = devm_clk_register(isp->dev, &xclk->hw); -		if (IS_ERR(clk)) -			return PTR_ERR(clk); +		/* +		 * The first argument is NULL in order to avoid circular +		 * reference, as this driver takes reference on the +		 * sensor subdevice modules and the sensors would take +		 * reference on this module through clk_get(). +		 */ +		xclk->clk = clk_register(NULL, &xclk->hw); +		if (IS_ERR(xclk->clk)) +			return PTR_ERR(xclk->clk);  		if (pdata->xclks[i].con_id == NULL &&  		    pdata->xclks[i].dev_id == NULL) @@ -320,7 +329,7 @@ static int isp_xclk_init(struct isp_device *isp)  		xclk->lookup->con_id = pdata->xclks[i].con_id;  		xclk->lookup->dev_id = pdata->xclks[i].dev_id; -		xclk->lookup->clk = clk; +		xclk->lookup->clk = xclk->clk;  		clkdev_add(xclk->lookup);  	} @@ -335,6 +344,9 @@ static void isp_xclk_cleanup(struct isp_device *isp)  	for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) {  		struct isp_xclk *xclk = &isp->xclks[i]; +		if (!IS_ERR(xclk->clk)) +			clk_unregister(xclk->clk); +  		if (xclk->lookup)  			clkdev_drop(xclk->lookup);  	} @@ -381,7 +393,7 @@ static void isp_disable_interrupts(struct isp_device *isp)   * @isp: OMAP3 ISP device   * @idle: Consider idle state.   * - * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS + * Set the power settings for the ISP and SBL bus and configure the HS/VS   * interrupt source.   *   * We need to configure the HS/VS interrupt source before interrupts get @@ -578,9 +590,6 @@ static void isp_isr_sbl(struct isp_device *isp)   * @_isp: Pointer to the OMAP3 ISP device   *   * Handles the corresponding callback if plugged in. - * - * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the - * IRQ wasn't handled.   */  static irqreturn_t isp_isr(int irq, void *_isp)  { @@ -863,15 +872,12 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,  	unsigned long flags;  	int ret; -	/* If the preview engine crashed it might not respond to read/write -	 * operations on the L4 bus. This would result in a bus fault and a -	 * kernel oops. Refuse to start streaming in that case. This check must -	 * be performed before the loop below to avoid starting entities if the -	 * pipeline won't start anyway (those entities would then likely fail to -	 * stop, making the problem worse). +	/* Refuse to start streaming if an entity included in the pipeline has +	 * crashed. This check must be performed before the loop below to avoid +	 * starting entities if the pipeline won't start anyway (those entities +	 * would then likely fail to stop, making the problem worse).  	 */ -	if ((pipe->entities & isp->crashed) & -	    (1U << isp->isp_prev.subdev.entity.id)) +	if (pipe->entities & isp->crashed)  		return -EIO;  	spin_lock_irqsave(&pipe->lock, flags); @@ -1004,13 +1010,23 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)  		else  			ret = 0; +		/* Handle stop failures. An entity that fails to stop can +		 * usually just be restarted. Flag the stop failure nonetheless +		 * to trigger an ISP reset the next time the device is released, +		 * just in case. +		 * +		 * The preview engine is a special case. A failure to stop can +		 * mean a hardware crash. When that happens the preview engine +		 * won't respond to read/write operations on the L4 bus anymore, +		 * resulting in a bus fault and a kernel oops next time it gets +		 * accessed. Mark it as crashed to prevent pipelines including +		 * it from being started. +		 */  		if (ret) {  			dev_info(isp->dev, "Unable to stop %s\n", subdev->name); -			/* If the entity failed to stopped, assume it has -			 * crashed. Mark it as such, the ISP will be reset when -			 * applications will release it. -			 */ -			isp->crashed |= 1U << subdev->entity.id; +			isp->stop_failure = true; +			if (subdev == &isp->isp_prev.subdev) +				isp->crashed |= 1U << subdev->entity.id;  			failure = -ETIMEDOUT;  		}  	} @@ -1047,6 +1063,23 @@ int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,  }  /* + * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline + * @pipe: ISP pipeline + * + * Cancelling a stream mark all buffers on all video nodes in the pipeline as + * erroneous and makes sure no new buffer can be queued. This function is called + * when a fatal error that prevents any further operation on the pipeline + * occurs. + */ +void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe) +{ +	if (pipe->input) +		omap3isp_video_cancel_stream(pipe->input); +	if (pipe->output) +		omap3isp_video_cancel_stream(pipe->output); +} + +/*   * isp_pipeline_resume - Resume streaming on a pipeline   * @pipe: ISP pipeline   * @@ -1198,6 +1231,7 @@ static int isp_reset(struct isp_device *isp)  		udelay(1);  	} +	isp->stop_failure = false;  	isp->crashed = 0;  	return 0;  } @@ -1365,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); @@ -1385,7 +1419,7 @@ int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,  }  /* - * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping + * omap3isp_module_sync_is_stopping - Helper to verify if module was stopping   * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization   * @stopping: flag which tells module wants to stop   * @@ -1593,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; @@ -1602,20 +1636,25 @@ 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;  		}  		/* Reset the ISP if an entity has failed to stop. This is the  		 * only way to recover from such conditions.  		 */ -		if (isp->crashed) +		if (isp->crashed || isp->stop_failure)  			isp_reset(isp);  		isp_disable_clocks(isp);  	}  	mutex_unlock(&isp->isp_mutex);  } +void omap3isp_put(struct isp_device *isp) +{ +	__omap3isp_put(isp, true); +} +  /* --------------------------------------------------------------------------   * Platform device driver   */ @@ -1673,7 +1712,7 @@ void omap3isp_print_status(struct isp_device *isp)   * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in   * resume(), and the the pipelines are restarted in complete().   * - * TODO: PM dependencies between the ISP and sensors are not modeled explicitly + * TODO: PM dependencies between the ISP and sensors are not modelled explicitly   * yet.   */  static int isp_pm_prepare(struct device *dev) @@ -2088,6 +2127,61 @@ error_csiphy:  	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 @@ -2103,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;  } @@ -2120,28 +2212,13 @@ static int isp_map_mem_resource(struct platform_device *pdev,  	/* request the mem region for the camera registers */  	mem = platform_get_resource(pdev, IORESOURCE_MEM, res); -	if (!mem) { -		dev_err(isp->dev, "no mem resource?\n"); -		return -ENODEV; -	} - -	if (!devm_request_mem_region(isp->dev, mem->start, resource_size(mem), -				     pdev->name)) { -		dev_err(isp->dev, -			"cannot reserve camera register I/O region\n"); -		return -ENODEV; -	} -	isp->mmio_base_phys[res] = mem->start; -	isp->mmio_size[res] = resource_size(mem);  	/* map the region */ -	isp->mmio_base[res] = devm_ioremap_nocache(isp->dev, -						   isp->mmio_base_phys[res], -						   isp->mmio_size[res]); -	if (!isp->mmio_base[res]) { -		dev_err(isp->dev, "cannot map camera register I/O region\n"); -		return -ENODEV; -	} +	isp->mmio_base[res] = devm_ioremap_resource(isp->dev, mem); +	if (IS_ERR(isp->mmio_base[res])) +		return PTR_ERR(isp->mmio_base[res]); + +	isp->mmio_base_phys[res] = mem->start;  	return 0;  } @@ -2182,9 +2259,9 @@ static int isp_probe(struct platform_device *pdev)  	isp->pdata = pdata;  	isp->ref_count = 0; -	isp->raw_dmamask = DMA_BIT_MASK(32); -	isp->dev->dma_mask = &isp->raw_dmamask; -	isp->dev->coherent_dma_mask = DMA_BIT_MASK(32); +	ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); +	if (ret) +		return ret;  	platform_set_drvdata(pdev, isp); @@ -2248,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) @@ -2293,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); diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index cd3eff45ae7..2c314eea125 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -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 @@ -135,6 +133,7 @@ struct isp_xclk {  	struct isp_device *isp;  	struct clk_hw hw;  	struct clk_lookup *lookup; +	struct clk *clk;  	enum isp_xclk_id id;  	spinlock_t lock;	/* Protects enabled and divider */ @@ -151,10 +150,10 @@ struct isp_xclk {   *             regions.   * @mmio_base_phys: Array with physical L4 bus addresses for ISP register   *                  regions. - * @mmio_size: Array with ISP register regions size in bytes. - * @raw_dmamask: Raw DMA mask + * @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.   * @crashed: Bitmask of crashed entities (indexed by entity ID)   * @has_context: Context has been saved at least once and can be restored.   * @ref_count: Reference count for handling multiple ISP requests. @@ -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. @@ -188,13 +186,13 @@ struct isp_device {  	void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];  	unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; -	resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; -	u64 raw_dmamask; +	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 */ +	bool stop_failure;  	u32 crashed;  	int has_context;  	int ref_count; @@ -221,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) \ @@ -240,6 +236,7 @@ int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,  int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,  				 enum isp_pipeline_stream_state state); +void omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe);  void omap3isp_configure_bridge(struct isp_device *isp,  			       enum ccdc_input_entity input,  			       const struct isp_parallel_platform_data *pdata, @@ -266,7 +263,7 @@ void omap3isp_unregister_entities(struct platform_device *pdev);  /*   * isp_reg_readl - Read value of an OMAP3 ISP register - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   * @isp_mmio_range: Range to which the register offset refers to.   * @reg_offset: Register offset to read from.   * @@ -281,7 +278,7 @@ u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,  /*   * isp_reg_writel - Write value to an OMAP3 ISP register - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   * @reg_value: 32 bit value to write to the register.   * @isp_mmio_range: Range to which the register offset refers to.   * @reg_offset: Register offset to write into. @@ -294,8 +291,8 @@ void isp_reg_writel(struct isp_device *isp, u32 reg_value,  }  /* - * isp_reg_and - Clear individual bits in an OMAP3 ISP register - * @dev: Device pointer specific to the OMAP3 ISP. + * isp_reg_clr - Clear individual bits in an OMAP3 ISP register + * @isp: Device pointer specific to the OMAP3 ISP.   * @mmio_range: Range to which the register offset refers to.   * @reg: Register offset to work on.   * @clr_bits: 32 bit value which would be cleared in the register. @@ -311,7 +308,7 @@ void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,  /*   * isp_reg_set - Set individual bits in an OMAP3 ISP register - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   * @mmio_range: Range to which the register offset refers to.   * @reg: Register offset to work on.   * @set_bits: 32 bit value which would be set in the register. @@ -327,7 +324,7 @@ void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,  /*   * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   * @mmio_range: Range to which the register offset refers to.   * @reg: Register offset to work on.   * @clr_bits: 32 bit value which would be cleared in the register. diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 907a205da5a..9f727d20f06 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -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); @@ -293,7 +293,7 @@ static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)  			isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,  				    ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);  			ccdc->lsc.state = LSC_STATE_STOPPED; -			dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); +			dev_warn(to_device(ccdc), "LSC prefetch timeout\n");  			return -ETIMEDOUT;  		}  		ccdc->lsc.state = LSC_STATE_RUNNING; @@ -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), @@ -674,7 +667,7 @@ static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)  /*   * ccdc_config - Set CCDC configuration from userspace   * @ccdc: Pointer to ISP CCDC device. - * @userspace_add: Structure containing CCDC configuration sent from userspace. + * @ccdc_struct: Structure containing CCDC configuration sent from userspace.   *   * Returns 0 if successful, -EINVAL if the pointer to the configuration   * structure is null, or the copy_from_user function fails to copy user space @@ -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(). +			 * The table address must be 64-bytes aligned, which is +			 * guaranteed by dma_alloc_coherent().  			 */ -			size = ccdc->fpc.fpnum * 4; -			table_new = omap_iommu_vmalloc(isp->domain, isp->dev, -							0, size, IOMMU_FLAG); -			if (IS_ERR_VALUE(table_new)) +			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); @@ -793,7 +791,7 @@ static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)  /*   * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers - * @dev: Pointer to ISP device + * @isp: Pointer to ISP device   */  void omap3isp_ccdc_restore_context(struct isp_device *isp)  { @@ -1516,12 +1514,14 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)  	if (ccdc_sbl_wait_idle(ccdc, 1000)) {  		dev_info(isp->dev, "CCDC won't become idle!\n"); +		isp->crashed |= 1U << ccdc->subdev.entity.id; +		omap3isp_pipeline_cancel_stream(pipe);  		goto done;  	}  	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;  	} @@ -1660,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 @@ -2484,7 +2484,8 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)  	v4l2_set_subdevdata(sd, ccdc);  	sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; -	pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +	pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK +				    | MEDIA_PAD_FL_MUST_CONNECT;  	pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;  	pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; @@ -2522,7 +2523,7 @@ error_video:  /*   * omap3isp_ccdc_init - CCDC module initialization. - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   *   * TODO: Get the initialisation values from platform data.   * @@ -2561,7 +2562,7 @@ int omap3isp_ccdc_init(struct isp_device *isp)  /*   * omap3isp_ccdc_cleanup - CCDC module cleanup. - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP.   */  void omap3isp_ccdc_cleanup(struct isp_device *isp)  { @@ -2577,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);  } diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h index a5da9e19edb..f65061602c7 100644 --- a/drivers/media/platform/omap3isp/ispccdc.h +++ b/drivers/media/platform/omap3isp/ispccdc.h @@ -46,6 +46,12 @@ enum ccdc_input_entity {  #define	OMAP3ISP_CCDC_NEVENTS	16 +struct ispccdc_fpc { +	void *addr; +	dma_addr_t dma; +	unsigned int fpnum; +}; +  enum ispccdc_lsc_state {  	LSC_STATE_STOPPED = 0,  	LSC_STATE_STOPPING = 1, @@ -57,18 +63,16 @@ struct ispccdc_lsc_config_req {  	struct list_head list;  	struct omap3isp_ccdc_lsc_config config;  	unsigned char enable; -	u32 table; -	struct iovm_struct *iovm; + +	struct { +		void *addr; +		dma_addr_t dma; +		struct sg_table sgt; +	} table;  };  /*   * ispccdc_lsc - CCDC LSC parameters - * @update_config: Set when user changes config - * @request_enable: Whether LSC is requested to be enabled - * @config: LSC config set by user - * @update_table: Set when user provides a new LSC table to table_new - * @table_new: LSC table set by user, ISP address - * @table_inuse: LSC table currently in use, ISP address   */  struct ispccdc_lsc {  	enum ispccdc_lsc_state state; @@ -142,7 +146,7 @@ struct isp_ccdc_device {  		     fpc_en:1;  	struct omap3isp_ccdc_blcomp blcomp;  	struct omap3isp_ccdc_bclamp clamp; -	struct omap3isp_ccdc_fpc fpc; +	struct ispccdc_fpc fpc;  	struct ispccdc_lsc lsc;  	unsigned int update;  	unsigned int shadow_update; diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index e71651429dd..f3801db9095 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -211,7 +211,7 @@ static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable)  /*   * ccp2_phyif_config - Initialize CCP2 phy interface config   * @ccp2: Pointer to ISP CCP2 device - * @config: CCP2 platform data + * @pdata: CCP2 platform data   *   * Configure the CCP2 physical interface module from platform data.   * @@ -518,7 +518,7 @@ static void ccp2_mem_configure(struct isp_ccp2_device *ccp2,  		       ISPCCP2_LCM_IRQSTATUS_EOF_IRQ,  		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS); -	/* Enable LCM interupts */ +	/* Enable LCM interrupts */  	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE,  		    ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |  		    ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ); @@ -549,7 +549,7 @@ static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)  	buffer = omap3isp_video_buffer_next(&ccp2->video_in);  	if (buffer != NULL) -		ccp2_set_inaddr(ccp2, buffer->isp_addr); +		ccp2_set_inaddr(ccp2, buffer->dma);  	pipe->state |= ISP_PIPELINE_IDLE_INPUT; @@ -940,7 +940,7 @@ static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)  {  	struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2; -	ccp2_set_inaddr(ccp2, buffer->isp_addr); +	ccp2_set_inaddr(ccp2, buffer->dma);  	return 0;  } @@ -1076,7 +1076,8 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)  	v4l2_set_subdevdata(sd, ccp2);  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -	pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +	pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK +				    | MEDIA_PAD_FL_MUST_CONNECT;  	pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;  	me->ops = &ccp2_media_ops; @@ -1095,7 +1096,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)  	 * implementation we use a fixed 32 bytes alignment regardless of the  	 * input format and width. If strict 128 bits alignment support is  	 * required ispvideo will need to be made aware of this special dual -	 * alignement requirements. +	 * alignment requirements.  	 */  	ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;  	ccp2->video_in.bpl_alignment = 32; diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 6db245d84bb..5a2e47e58b8 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -695,7 +695,7 @@ static void csi2_isr_buffer(struct isp_csi2_device *csi2)  	if (buffer == NULL)  		return; -	csi2_set_outaddr(csi2, buffer->isp_addr); +	csi2_set_outaddr(csi2, buffer->dma);  	csi2_ctx_enable(isp, csi2, 0, 1);  } @@ -812,7 +812,7 @@ static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)  	struct isp_device *isp = video->isp;  	struct isp_csi2_device *csi2 = &isp->isp_csi2a; -	csi2_set_outaddr(csi2, buffer->isp_addr); +	csi2_set_outaddr(csi2, buffer->dma);  	/*  	 * If streaming was enabled before there was a buffer queued @@ -1245,7 +1245,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;  	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK +				    | MEDIA_PAD_FL_MUST_CONNECT;  	me->ops = &csi2_media_ops;  	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index 75fd82b152b..d6811ce263e 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -47,7 +47,7 @@ static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)  	if (aewb->state == ISPSTAT_DISABLED)  		return; -	isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, +	isp_reg_writel(aewb->isp, aewb->active_buf->dma_addr,  		       OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);  	if (!aewb->update) diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index a0bf5af3243..6fc960cd30f 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -51,7 +51,7 @@ static void h3a_af_setup_regs(struct ispstat *af, void *priv)  	if (af->state == ISPSTAT_DISABLED)  		return; -	isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, +	isp_reg_writel(af->isp, af->active_buf->dma_addr, OMAP3_ISP_IOMEM_H3A,  		       ISPH3A_AFBUFST);  	if (!af->update) diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index e070c24048e..06a5f8164ea 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -299,7 +299,7 @@ static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)  /*   * hist_validate_params - Helper function to check user given params. - * @user_cfg: Pointer to user configuration structure. + * @new_conf: Pointer to user configuration structure.   *   * Returns 0 on success configuration.   */ @@ -351,7 +351,7 @@ static int hist_validate_params(struct ispstat *hist, void *new_conf)  	buf_size = hist_get_buf_size(user_cfg);  	if (buf_size > user_cfg->buf_size) -		/* User's buf_size request wasn't enoght */ +		/* User's buf_size request wasn't enough */  		user_cfg->buf_size = buf_size;  	else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)  		user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index cd8831aebde..720809b07e7 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -122,7 +122,7 @@ static struct omap3isp_prev_csc flr_prev_csc = {  #define PREV_MAX_OUT_WIDTH_REV_15	4096  /* - * Coeficient Tables for the submodules in Preview. + * Coefficient Tables for the submodules in Preview.   * Array is initialised with the values from.the tables text file.   */ @@ -971,7 +971,8 @@ static void preview_setup_hw(struct isp_prev_device *prev, u32 update,  /*   * preview_config_ycpos - Configure byte layout of YUV image. - * @mode: Indicates the required byte layout. + * @prev: pointer to previewer private structure + * @pixelcode: pixel code   */  static void  preview_config_ycpos(struct isp_prev_device *prev, @@ -1079,6 +1080,7 @@ static void preview_config_input_format(struct isp_prev_device *prev,   */  static void preview_config_input_size(struct isp_prev_device *prev, u32 active)  { +	const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];  	struct isp_device *isp = to_isp_device(prev);  	unsigned int sph = prev->crop.left;  	unsigned int eph = prev->crop.left + prev->crop.width - 1; @@ -1086,6 +1088,14 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active)  	unsigned int elv = prev->crop.top + prev->crop.height - 1;  	u32 features; +	if (format->code != V4L2_MBUS_FMT_Y8_1X8 && +	    format->code != V4L2_MBUS_FMT_Y10_1X10) { +		sph -= 2; +		eph += 2; +		slv -= 2; +		elv += 2; +	} +  	features = (prev->params.params[0].features & active)  		 | (prev->params.params[1].features & ~active); @@ -1363,8 +1373,8 @@ static void preview_init_params(struct isp_prev_device *prev)  }  /* - * preview_max_out_width - Handle previewer hardware ouput limitations - * @isp_revision : ISP revision + * preview_max_out_width - Handle previewer hardware output limitations + * @prev: pointer to previewer private structure   * returns maximum width output for current isp revision   */  static unsigned int preview_max_out_width(struct isp_prev_device *prev) @@ -1489,14 +1499,14 @@ static void preview_isr_buffer(struct isp_prev_device *prev)  	if (prev->input == PREVIEW_INPUT_MEMORY) {  		buffer = omap3isp_video_buffer_next(&prev->video_in);  		if (buffer != NULL) -			preview_set_inaddr(prev, buffer->isp_addr); +			preview_set_inaddr(prev, buffer->dma);  		pipe->state |= ISP_PIPELINE_IDLE_INPUT;  	}  	if (prev->output & PREVIEW_OUTPUT_MEMORY) {  		buffer = omap3isp_video_buffer_next(&prev->video_out);  		if (buffer != NULL) { -			preview_set_outaddr(prev, buffer->isp_addr); +			preview_set_outaddr(prev, buffer->dma);  			restart = 1;  		}  		pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; @@ -1567,10 +1577,10 @@ static int preview_video_queue(struct isp_video *video,  	struct isp_prev_device *prev = &video->isp->isp_prev;  	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) -		preview_set_inaddr(prev, buffer->isp_addr); +		preview_set_inaddr(prev, buffer->dma);  	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) -		preview_set_outaddr(prev, buffer->isp_addr); +		preview_set_outaddr(prev, buffer->dma);  	return 0;  } @@ -1610,7 +1620,7 @@ static const struct v4l2_ctrl_ops preview_ctrl_ops = {  /*   * preview_ioctl - Handle preview module private ioctl's - * @prev: pointer to preview context structure + * @sd: pointer to v4l2 subdev structure   * @cmd: configuration command   * @arg: configuration argument   * return -EINVAL or zero on success @@ -2283,7 +2293,8 @@ static int preview_init_entities(struct isp_prev_device *prev)  	v4l2_ctrl_handler_setup(&prev->ctrls);  	sd->ctrl_handler = &prev->ctrls; -	pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +	pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK +				    | MEDIA_PAD_FL_MUST_CONNECT;  	pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;  	me->ops = &preview_media_ops; @@ -2340,7 +2351,7 @@ error_video_in:  /*   * omap3isp_preview_init - Previewer initialization. - * @dev : Pointer to ISP device + * @isp : Pointer to ISP device   * return -ENOMEM or zero on success   */  int omap3isp_preview_init(struct isp_device *isp) diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c deleted file mode 100644 index e15f0134205..00000000000 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ /dev/null @@ -1,1159 +0,0 @@ -/* - * ispqueue.c - * - * TI OMAP3 ISP - Video buffers queue handling - * - * Copyright (C) 2010 Nokia Corporation - * - * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> - *	     Sakari Ailus <sakari.ailus@iki.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <asm/cacheflush.h> -#include <linux/dma-mapping.h> -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/poll.h> -#include <linux/scatterlist.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include "ispqueue.h" - -/* ----------------------------------------------------------------------------- - * Video buffers management - */ - -/* - * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP - * - * The typical operation required here is Cache Invalidation across - * the (user space) buffer address range. And this _must_ be done - * at QBUF stage (and *only* at QBUF). - * - * We try to use optimal cache invalidation function: - * - dmac_map_area: - *    - used when the number of pages are _low_. - *    - it becomes quite slow as the number of pages increase. - *       - for 648x492 viewfinder (150 pages) it takes 1.3 ms. - *       - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms. - * - * - flush_cache_all: - *    - used when the number of pages are _high_. - *    - time taken in the range of 500-900 us. - *    - has a higher penalty but, as whole dcache + icache is invalidated - */ -/* - * FIXME: dmac_inv_range crashes randomly on the user space buffer - *        address. Fall back to flush_cache_all for now. - */ -#define ISP_CACHE_FLUSH_PAGES_MAX       0 - -static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf) -{ -	if (buf->skip_cache) -		return; - -	if (buf->vbuf.m.userptr == 0 || buf->npages == 0 || -	    buf->npages > ISP_CACHE_FLUSH_PAGES_MAX) -		flush_cache_all(); -	else { -		dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length, -			      DMA_FROM_DEVICE); -		outer_inv_range(buf->vbuf.m.userptr, -				buf->vbuf.m.userptr + buf->vbuf.length); -	} -} - -/* - * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped - * - * Lock the VMAs underlying the given buffer into memory. This avoids the - * userspace buffer mapping from being swapped out, making VIPT cache handling - * easier. - * - * Note that the pages will not be freed as the buffers have been locked to - * memory using by a call to get_user_pages(), but the userspace mapping could - * still disappear if the VMAs are not locked. This is caused by the memory - * management code trying to be as lock-less as possible, which results in the - * userspace mapping manager not finding out that the pages are locked under - * some conditions. - */ -static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock) -{ -	struct vm_area_struct *vma; -	unsigned long start; -	unsigned long end; -	int ret = 0; - -	if (buf->vbuf.memory == V4L2_MEMORY_MMAP) -		return 0; - -	/* We can be called from workqueue context if the current task dies to -	 * unlock the VMAs. In that case there's no current memory management -	 * context so unlocking can't be performed, but the VMAs have been or -	 * are getting destroyed anyway so it doesn't really matter. -	 */ -	if (!current || !current->mm) -		return lock ? -EINVAL : 0; - -	start = buf->vbuf.m.userptr; -	end = buf->vbuf.m.userptr + buf->vbuf.length - 1; - -	down_write(¤t->mm->mmap_sem); -	spin_lock(¤t->mm->page_table_lock); - -	do { -		vma = find_vma(current->mm, start); -		if (vma == NULL) { -			ret = -EFAULT; -			goto out; -		} - -		if (lock) -			vma->vm_flags |= VM_LOCKED; -		else -			vma->vm_flags &= ~VM_LOCKED; - -		start = vma->vm_end + 1; -	} while (vma->vm_end < end); - -	if (lock) -		buf->vm_flags |= VM_LOCKED; -	else -		buf->vm_flags &= ~VM_LOCKED; - -out: -	spin_unlock(¤t->mm->page_table_lock); -	up_write(¤t->mm->mmap_sem); -	return ret; -} - -/* - * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer - * - * Iterate over the vmalloc'ed area and create a scatter list entry for every - * page. - */ -static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) -{ -	struct scatterlist *sglist; -	unsigned int npages; -	unsigned int i; -	void *addr; - -	addr = buf->vaddr; -	npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT; - -	sglist = vmalloc(npages * sizeof(*sglist)); -	if (sglist == NULL) -		return -ENOMEM; - -	sg_init_table(sglist, npages); - -	for (i = 0; i < npages; ++i, addr += PAGE_SIZE) { -		struct page *page = vmalloc_to_page(addr); - -		if (page == NULL || PageHighMem(page)) { -			vfree(sglist); -			return -EINVAL; -		} - -		sg_set_page(&sglist[i], page, PAGE_SIZE, 0); -	} - -	buf->sglen = npages; -	buf->sglist = sglist; - -	return 0; -} - -/* - * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer - * - * Walk the buffer pages list and create a 1:1 mapping to a scatter list. - */ -static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) -{ -	struct scatterlist *sglist; -	unsigned int offset = buf->offset; -	unsigned int i; - -	sglist = vmalloc(buf->npages * sizeof(*sglist)); -	if (sglist == NULL) -		return -ENOMEM; - -	sg_init_table(sglist, buf->npages); - -	for (i = 0; i < buf->npages; ++i) { -		if (PageHighMem(buf->pages[i])) { -			vfree(sglist); -			return -EINVAL; -		} - -		sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset, -			    offset); -		offset = 0; -	} - -	buf->sglen = buf->npages; -	buf->sglist = sglist; - -	return 0; -} - -/* - * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer - * - * Create a scatter list of physically contiguous pages starting at the buffer - * memory physical address. - */ -static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) -{ -	struct scatterlist *sglist; -	unsigned int offset = buf->offset; -	unsigned long pfn = buf->paddr >> PAGE_SHIFT; -	unsigned int i; - -	sglist = vmalloc(buf->npages * sizeof(*sglist)); -	if (sglist == NULL) -		return -ENOMEM; - -	sg_init_table(sglist, buf->npages); - -	for (i = 0; i < buf->npages; ++i, ++pfn) { -		sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset, -			    offset); -		/* PFNMAP buffers will not get DMA-mapped, set the DMA address -		 * manually. -		 */ -		sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset; -		offset = 0; -	} - -	buf->sglen = buf->npages; -	buf->sglist = sglist; - -	return 0; -} - -/* - * isp_video_buffer_cleanup - Release pages for a userspace VMA. - * - * Release pages locked by a call isp_video_buffer_prepare_user and free the - * pages table. - */ -static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) -{ -	enum dma_data_direction direction; -	unsigned int i; - -	if (buf->queue->ops->buffer_cleanup) -		buf->queue->ops->buffer_cleanup(buf); - -	if (!(buf->vm_flags & VM_PFNMAP)) { -		direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE -			  ? DMA_FROM_DEVICE : DMA_TO_DEVICE; -		dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen, -			     direction); -	} - -	vfree(buf->sglist); -	buf->sglist = NULL; -	buf->sglen = 0; - -	if (buf->pages != NULL) { -		isp_video_buffer_lock_vma(buf, 0); - -		for (i = 0; i < buf->npages; ++i) -			page_cache_release(buf->pages[i]); - -		vfree(buf->pages); -		buf->pages = NULL; -	} - -	buf->npages = 0; -	buf->skip_cache = false; -} - -/* - * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. - * - * This function creates a list of pages for a userspace VMA. The number of - * pages is first computed based on the buffer size, and pages are then - * retrieved by a call to get_user_pages. - * - * Pages are pinned to memory by get_user_pages, making them available for DMA - * transfers. However, due to memory management optimization, it seems the - * get_user_pages doesn't guarantee that the pinned pages will not be written - * to swap and removed from the userspace mapping(s). When this happens, a page - * fault can be generated when accessing those unmapped pages. - * - * If the fault is triggered by a page table walk caused by VIPT cache - * management operations, the page fault handler might oops if the MM semaphore - * is held, as it can't handle kernel page faults in that case. To fix that, a - * fixup entry needs to be added to the cache management code, or the userspace - * VMA must be locked to avoid removing pages from the userspace mapping in the - * first place. - * - * If the number of pages retrieved is smaller than the number required by the - * buffer size, the function returns -EFAULT. - */ -static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) -{ -	unsigned long data; -	unsigned int first; -	unsigned int last; -	int ret; - -	data = buf->vbuf.m.userptr; -	first = (data & PAGE_MASK) >> PAGE_SHIFT; -	last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; - -	buf->offset = data & ~PAGE_MASK; -	buf->npages = last - first + 1; -	buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); -	if (buf->pages == NULL) -		return -ENOMEM; - -	down_read(¤t->mm->mmap_sem); -	ret = get_user_pages(current, current->mm, data & PAGE_MASK, -			     buf->npages, -			     buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, -			     buf->pages, NULL); -	up_read(¤t->mm->mmap_sem); - -	if (ret != buf->npages) { -		buf->npages = ret < 0 ? 0 : ret; -		isp_video_buffer_cleanup(buf); -		return -EFAULT; -	} - -	ret = isp_video_buffer_lock_vma(buf, 1); -	if (ret < 0) -		isp_video_buffer_cleanup(buf); - -	return ret; -} - -/* - * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer - * - * Userspace VM_PFNMAP buffers are supported only if they are contiguous in - * memory and if they span a single VMA. - * - * Return 0 if the buffer is valid, or -EFAULT otherwise. - */ -static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) -{ -	struct vm_area_struct *vma; -	unsigned long prev_pfn; -	unsigned long this_pfn; -	unsigned long start; -	unsigned long end; -	dma_addr_t pa = 0; -	int ret = -EFAULT; - -	start = buf->vbuf.m.userptr; -	end = buf->vbuf.m.userptr + buf->vbuf.length - 1; - -	buf->offset = start & ~PAGE_MASK; -	buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; -	buf->pages = NULL; - -	down_read(¤t->mm->mmap_sem); -	vma = find_vma(current->mm, start); -	if (vma == NULL || vma->vm_end < end) -		goto done; - -	for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { -		ret = follow_pfn(vma, start, &this_pfn); -		if (ret) -			goto done; - -		if (prev_pfn == 0) -			pa = this_pfn << PAGE_SHIFT; -		else if (this_pfn != prev_pfn + 1) { -			ret = -EFAULT; -			goto done; -		} - -		prev_pfn = this_pfn; -	} - -	buf->paddr = pa + buf->offset; -	ret = 0; - -done: -	up_read(¤t->mm->mmap_sem); -	return ret; -} - -/* - * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address - * - * This function locates the VMAs for the buffer's userspace address and checks - * that their flags match. The only flag that we need to care for at the moment - * is VM_PFNMAP. - * - * The buffer vm_flags field is set to the first VMA flags. - * - * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs - * have incompatible flags. - */ -static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) -{ -	struct vm_area_struct *vma; -	pgprot_t uninitialized_var(vm_page_prot); -	unsigned long start; -	unsigned long end; -	int ret = -EFAULT; - -	start = buf->vbuf.m.userptr; -	end = buf->vbuf.m.userptr + buf->vbuf.length - 1; - -	down_read(¤t->mm->mmap_sem); - -	do { -		vma = find_vma(current->mm, start); -		if (vma == NULL) -			goto done; - -		if (start == buf->vbuf.m.userptr) { -			buf->vm_flags = vma->vm_flags; -			vm_page_prot = vma->vm_page_prot; -		} - -		if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP) -			goto done; - -		if (vm_page_prot != vma->vm_page_prot) -			goto done; - -		start = vma->vm_end + 1; -	} while (vma->vm_end < end); - -	/* Skip cache management to enhance performances for non-cached or -	 * write-combining buffers. -	 */ -	if (vm_page_prot == pgprot_noncached(vm_page_prot) || -	    vm_page_prot == pgprot_writecombine(vm_page_prot)) -		buf->skip_cache = true; - -	ret = 0; - -done: -	up_read(¤t->mm->mmap_sem); -	return ret; -} - -/* - * isp_video_buffer_prepare - Make a buffer ready for operation - * - * Preparing a buffer involves: - * - * - validating VMAs (userspace buffers only) - * - locking pages and VMAs into memory (userspace buffers only) - * - building page and scatter-gather lists - * - mapping buffers for DMA operation - * - performing driver-specific preparation - * - * The function must be called in userspace context with a valid mm context - * (this excludes cleanup paths such as sys_close when the userspace process - * segfaults). - */ -static int isp_video_buffer_prepare(struct isp_video_buffer *buf) -{ -	enum dma_data_direction direction; -	int ret; - -	switch (buf->vbuf.memory) { -	case V4L2_MEMORY_MMAP: -		ret = isp_video_buffer_sglist_kernel(buf); -		break; - -	case V4L2_MEMORY_USERPTR: -		ret = isp_video_buffer_prepare_vm_flags(buf); -		if (ret < 0) -			return ret; - -		if (buf->vm_flags & VM_PFNMAP) { -			ret = isp_video_buffer_prepare_pfnmap(buf); -			if (ret < 0) -				return ret; - -			ret = isp_video_buffer_sglist_pfnmap(buf); -		} else { -			ret = isp_video_buffer_prepare_user(buf); -			if (ret < 0) -				return ret; - -			ret = isp_video_buffer_sglist_user(buf); -		} -		break; - -	default: -		return -EINVAL; -	} - -	if (ret < 0) -		goto done; - -	if (!(buf->vm_flags & VM_PFNMAP)) { -		direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE -			  ? DMA_FROM_DEVICE : DMA_TO_DEVICE; -		ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen, -				 direction); -		if (ret != buf->sglen) { -			ret = -EFAULT; -			goto done; -		} -	} - -	if (buf->queue->ops->buffer_prepare) -		ret = buf->queue->ops->buffer_prepare(buf); - -done: -	if (ret < 0) { -		isp_video_buffer_cleanup(buf); -		return ret; -	} - -	return ret; -} - -/* - * isp_video_queue_query - Query the status of a given buffer - * - * Locking: must be called with the queue lock held. - */ -static void isp_video_buffer_query(struct isp_video_buffer *buf, -				   struct v4l2_buffer *vbuf) -{ -	memcpy(vbuf, &buf->vbuf, sizeof(*vbuf)); - -	if (buf->vma_use_count) -		vbuf->flags |= V4L2_BUF_FLAG_MAPPED; - -	switch (buf->state) { -	case ISP_BUF_STATE_ERROR: -		vbuf->flags |= V4L2_BUF_FLAG_ERROR; -	case ISP_BUF_STATE_DONE: -		vbuf->flags |= V4L2_BUF_FLAG_DONE; -	case ISP_BUF_STATE_QUEUED: -	case ISP_BUF_STATE_ACTIVE: -		vbuf->flags |= V4L2_BUF_FLAG_QUEUED; -		break; -	case ISP_BUF_STATE_IDLE: -	default: -		break; -	} -} - -/* - * isp_video_buffer_wait - Wait for a buffer to be ready - * - * In non-blocking mode, return immediately with 0 if the buffer is ready or - * -EAGAIN if the buffer is in the QUEUED or ACTIVE state. - * - * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait - * queue using the same condition. - */ -static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking) -{ -	if (nonblocking) { -		return (buf->state != ISP_BUF_STATE_QUEUED && -			buf->state != ISP_BUF_STATE_ACTIVE) -			? 0 : -EAGAIN; -	} - -	return wait_event_interruptible(buf->wait, -		buf->state != ISP_BUF_STATE_QUEUED && -		buf->state != ISP_BUF_STATE_ACTIVE); -} - -/* ----------------------------------------------------------------------------- - * Queue management - */ - -/* - * isp_video_queue_free - Free video buffers memory - * - * Buffers can only be freed if the queue isn't streaming and if no buffer is - * mapped to userspace. Return -EBUSY if those conditions aren't statisfied. - * - * This function must be called with the queue lock held. - */ -static int isp_video_queue_free(struct isp_video_queue *queue) -{ -	unsigned int i; - -	if (queue->streaming) -		return -EBUSY; - -	for (i = 0; i < queue->count; ++i) { -		if (queue->buffers[i]->vma_use_count != 0) -			return -EBUSY; -	} - -	for (i = 0; i < queue->count; ++i) { -		struct isp_video_buffer *buf = queue->buffers[i]; - -		isp_video_buffer_cleanup(buf); - -		vfree(buf->vaddr); -		buf->vaddr = NULL; - -		kfree(buf); -		queue->buffers[i] = NULL; -	} - -	INIT_LIST_HEAD(&queue->queue); -	queue->count = 0; -	return 0; -} - -/* - * isp_video_queue_alloc - Allocate video buffers memory - * - * This function must be called with the queue lock held. - */ -static int isp_video_queue_alloc(struct isp_video_queue *queue, -				 unsigned int nbuffers, -				 unsigned int size, enum v4l2_memory memory) -{ -	struct isp_video_buffer *buf; -	unsigned int i; -	void *mem; -	int ret; - -	/* Start by freeing the buffers. */ -	ret = isp_video_queue_free(queue); -	if (ret < 0) -		return ret; - -	/* Bail out if no buffers should be allocated. */ -	if (nbuffers == 0) -		return 0; - -	/* Initialize the allocated buffers. */ -	for (i = 0; i < nbuffers; ++i) { -		buf = kzalloc(queue->bufsize, GFP_KERNEL); -		if (buf == NULL) -			break; - -		if (memory == V4L2_MEMORY_MMAP) { -			/* Allocate video buffers memory for mmap mode. Align -			 * the size to the page size. -			 */ -			mem = vmalloc_32_user(PAGE_ALIGN(size)); -			if (mem == NULL) { -				kfree(buf); -				break; -			} - -			buf->vbuf.m.offset = i * PAGE_ALIGN(size); -			buf->vaddr = mem; -		} - -		buf->vbuf.index = i; -		buf->vbuf.length = size; -		buf->vbuf.type = queue->type; -		buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -		buf->vbuf.field = V4L2_FIELD_NONE; -		buf->vbuf.memory = memory; - -		buf->queue = queue; -		init_waitqueue_head(&buf->wait); - -		queue->buffers[i] = buf; -	} - -	if (i == 0) -		return -ENOMEM; - -	queue->count = i; -	return nbuffers; -} - -/** - * omap3isp_video_queue_cleanup - Clean up the video buffers queue - * @queue: Video buffers queue - * - * Free all allocated resources and clean up the video buffers queue. The queue - * must not be busy (no ongoing video stream) and buffers must have been - * unmapped. - * - * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been - * unmapped. - */ -int omap3isp_video_queue_cleanup(struct isp_video_queue *queue) -{ -	return isp_video_queue_free(queue); -} - -/** - * omap3isp_video_queue_init - Initialize the video buffers queue - * @queue: Video buffers queue - * @type: V4L2 buffer type (capture or output) - * @ops: Driver-specific queue operations - * @dev: Device used for DMA operations - * @bufsize: Size of the driver-specific buffer structure - * - * Initialize the video buffers queue with the supplied parameters. - * - * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or - * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet. - * - * Buffer objects will be allocated using the given buffer size to allow room - * for driver-specific fields. Driver-specific buffer structures must start - * with a struct isp_video_buffer field. Drivers with no driver-specific buffer - * structure must pass the size of the isp_video_buffer structure in the bufsize - * parameter. - * - * Return 0 on success. - */ -int omap3isp_video_queue_init(struct isp_video_queue *queue, -			      enum v4l2_buf_type type, -			      const struct isp_video_queue_operations *ops, -			      struct device *dev, unsigned int bufsize) -{ -	INIT_LIST_HEAD(&queue->queue); -	mutex_init(&queue->lock); -	spin_lock_init(&queue->irqlock); - -	queue->type = type; -	queue->ops = ops; -	queue->dev = dev; -	queue->bufsize = bufsize; - -	return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 operations - */ - -/** - * omap3isp_video_queue_reqbufs - Allocate video buffers memory - * - * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It - * allocated video buffer objects and, for MMAP buffers, buffer memory. - * - * If the number of buffers is 0, all buffers are freed and the function returns - * without performing any allocation. - * - * If the number of buffers is not 0, currently allocated buffers (if any) are - * freed and the requested number of buffers are allocated. Depending on - * driver-specific requirements and on memory availability, a number of buffer - * smaller or bigger than requested can be allocated. This isn't considered as - * an error. - * - * Return 0 on success or one of the following error codes: - * - * -EINVAL if the buffer type or index are invalid - * -EBUSY if the queue is busy (streaming or buffers mapped) - * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition - */ -int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, -				 struct v4l2_requestbuffers *rb) -{ -	unsigned int nbuffers = rb->count; -	unsigned int size; -	int ret; - -	if (rb->type != queue->type) -		return -EINVAL; - -	queue->ops->queue_prepare(queue, &nbuffers, &size); -	if (size == 0) -		return -EINVAL; - -	nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS); - -	mutex_lock(&queue->lock); - -	ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory); -	if (ret < 0) -		goto done; - -	rb->count = ret; -	ret = 0; - -done: -	mutex_unlock(&queue->lock); -	return ret; -} - -/** - * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue - * - * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It - * returns the status of a given video buffer. - * - * Return 0 on success or -EINVAL if the buffer type or index are invalid. - */ -int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, -				  struct v4l2_buffer *vbuf) -{ -	struct isp_video_buffer *buf; -	int ret = 0; - -	if (vbuf->type != queue->type) -		return -EINVAL; - -	mutex_lock(&queue->lock); - -	if (vbuf->index >= queue->count) { -		ret = -EINVAL; -		goto done; -	} - -	buf = queue->buffers[vbuf->index]; -	isp_video_buffer_query(buf, vbuf); - -done: -	mutex_unlock(&queue->lock); -	return ret; -} - -/** - * omap3isp_video_queue_qbuf - Queue a buffer - * - * This function is intended to be used as a VIDIOC_QBUF ioctl handler. - * - * The v4l2_buffer structure passed from userspace is first sanity tested. If - * sane, the buffer is then processed and added to the main queue and, if the - * queue is streaming, to the IRQ queue. - * - * Before being enqueued, USERPTR buffers are checked for address changes. If - * the buffer has a different userspace address, the old memory area is unlocked - * and the new memory area is locked. - */ -int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, -			      struct v4l2_buffer *vbuf) -{ -	struct isp_video_buffer *buf; -	unsigned long flags; -	int ret = -EINVAL; - -	if (vbuf->type != queue->type) -		goto done; - -	mutex_lock(&queue->lock); - -	if (vbuf->index >= queue->count) -		goto done; - -	buf = queue->buffers[vbuf->index]; - -	if (vbuf->memory != buf->vbuf.memory) -		goto done; - -	if (buf->state != ISP_BUF_STATE_IDLE) -		goto done; - -	if (vbuf->memory == V4L2_MEMORY_USERPTR && -	    vbuf->length < buf->vbuf.length) -		goto done; - -	if (vbuf->memory == V4L2_MEMORY_USERPTR && -	    vbuf->m.userptr != buf->vbuf.m.userptr) { -		isp_video_buffer_cleanup(buf); -		buf->vbuf.m.userptr = vbuf->m.userptr; -		buf->prepared = 0; -	} - -	if (!buf->prepared) { -		ret = isp_video_buffer_prepare(buf); -		if (ret < 0) -			goto done; -		buf->prepared = 1; -	} - -	isp_video_buffer_cache_sync(buf); - -	buf->state = ISP_BUF_STATE_QUEUED; -	list_add_tail(&buf->stream, &queue->queue); - -	if (queue->streaming) { -		spin_lock_irqsave(&queue->irqlock, flags); -		queue->ops->buffer_queue(buf); -		spin_unlock_irqrestore(&queue->irqlock, flags); -	} - -	ret = 0; - -done: -	mutex_unlock(&queue->lock); -	return ret; -} - -/** - * omap3isp_video_queue_dqbuf - Dequeue a buffer - * - * This function is intended to be used as a VIDIOC_DQBUF ioctl handler. - * - * Wait until a buffer is ready to be dequeued, remove it from the queue and - * copy its information to the v4l2_buffer structure. - * - * If the nonblocking argument is not zero and no buffer is ready, return - * -EAGAIN immediately instead of waiting. - * - * If no buffer has been enqueued, or if the requested buffer type doesn't match - * the queue type, return -EINVAL. - */ -int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, -			       struct v4l2_buffer *vbuf, int nonblocking) -{ -	struct isp_video_buffer *buf; -	int ret; - -	if (vbuf->type != queue->type) -		return -EINVAL; - -	mutex_lock(&queue->lock); - -	if (list_empty(&queue->queue)) { -		ret = -EINVAL; -		goto done; -	} - -	buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); -	ret = isp_video_buffer_wait(buf, nonblocking); -	if (ret < 0) -		goto done; - -	list_del(&buf->stream); - -	isp_video_buffer_query(buf, vbuf); -	buf->state = ISP_BUF_STATE_IDLE; -	vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED; - -done: -	mutex_unlock(&queue->lock); -	return ret; -} - -/** - * omap3isp_video_queue_streamon - Start streaming - * - * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It - * starts streaming on the queue and calls the buffer_queue operation for all - * queued buffers. - * - * Return 0 on success. - */ -int omap3isp_video_queue_streamon(struct isp_video_queue *queue) -{ -	struct isp_video_buffer *buf; -	unsigned long flags; - -	mutex_lock(&queue->lock); - -	if (queue->streaming) -		goto done; - -	queue->streaming = 1; - -	spin_lock_irqsave(&queue->irqlock, flags); -	list_for_each_entry(buf, &queue->queue, stream) -		queue->ops->buffer_queue(buf); -	spin_unlock_irqrestore(&queue->irqlock, flags); - -done: -	mutex_unlock(&queue->lock); -	return 0; -} - -/** - * omap3isp_video_queue_streamoff - Stop streaming - * - * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It - * stops streaming on the queue and wakes up all the buffers. - * - * Drivers must stop the hardware and synchronize with interrupt handlers and/or - * delayed works before calling this function to make sure no buffer will be - * touched by the driver and/or hardware. - */ -void omap3isp_video_queue_streamoff(struct isp_video_queue *queue) -{ -	struct isp_video_buffer *buf; -	unsigned long flags; -	unsigned int i; - -	mutex_lock(&queue->lock); - -	if (!queue->streaming) -		goto done; - -	queue->streaming = 0; - -	spin_lock_irqsave(&queue->irqlock, flags); -	for (i = 0; i < queue->count; ++i) { -		buf = queue->buffers[i]; - -		if (buf->state == ISP_BUF_STATE_ACTIVE) -			wake_up(&buf->wait); - -		buf->state = ISP_BUF_STATE_IDLE; -	} -	spin_unlock_irqrestore(&queue->irqlock, flags); - -	INIT_LIST_HEAD(&queue->queue); - -done: -	mutex_unlock(&queue->lock); -} - -/** - * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE - * - * This function is intended to be used with suspend/resume operations. It - * discards all 'done' buffers as they would be too old to be requested after - * resume. - * - * Drivers must stop the hardware and synchronize with interrupt handlers and/or - * delayed works before calling this function to make sure no buffer will be - * touched by the driver and/or hardware. - */ -void omap3isp_video_queue_discard_done(struct isp_video_queue *queue) -{ -	struct isp_video_buffer *buf; -	unsigned int i; - -	mutex_lock(&queue->lock); - -	if (!queue->streaming) -		goto done; - -	for (i = 0; i < queue->count; ++i) { -		buf = queue->buffers[i]; - -		if (buf->state == ISP_BUF_STATE_DONE) -			buf->state = ISP_BUF_STATE_ERROR; -	} - -done: -	mutex_unlock(&queue->lock); -} - -static void isp_video_queue_vm_open(struct vm_area_struct *vma) -{ -	struct isp_video_buffer *buf = vma->vm_private_data; - -	buf->vma_use_count++; -} - -static void isp_video_queue_vm_close(struct vm_area_struct *vma) -{ -	struct isp_video_buffer *buf = vma->vm_private_data; - -	buf->vma_use_count--; -} - -static const struct vm_operations_struct isp_video_queue_vm_ops = { -	.open = isp_video_queue_vm_open, -	.close = isp_video_queue_vm_close, -}; - -/** - * omap3isp_video_queue_mmap - Map buffers to userspace - * - * This function is intended to be used as an mmap() file operation handler. It - * maps a buffer to userspace based on the VMA offset. - * - * Only buffers of memory type MMAP are supported. - */ -int omap3isp_video_queue_mmap(struct isp_video_queue *queue, -			 struct vm_area_struct *vma) -{ -	struct isp_video_buffer *uninitialized_var(buf); -	unsigned long size; -	unsigned int i; -	int ret = 0; - -	mutex_lock(&queue->lock); - -	for (i = 0; i < queue->count; ++i) { -		buf = queue->buffers[i]; -		if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) -			break; -	} - -	if (i == queue->count) { -		ret = -EINVAL; -		goto done; -	} - -	size = vma->vm_end - vma->vm_start; - -	if (buf->vbuf.memory != V4L2_MEMORY_MMAP || -	    size != PAGE_ALIGN(buf->vbuf.length)) { -		ret = -EINVAL; -		goto done; -	} - -	ret = remap_vmalloc_range(vma, buf->vaddr, 0); -	if (ret < 0) -		goto done; - -	vma->vm_ops = &isp_video_queue_vm_ops; -	vma->vm_private_data = buf; -	isp_video_queue_vm_open(vma); - -done: -	mutex_unlock(&queue->lock); -	return ret; -} - -/** - * omap3isp_video_queue_poll - Poll video queue state - * - * This function is intended to be used as a poll() file operation handler. It - * polls the state of the video buffer at the front of the queue and returns an - * events mask. - * - * If no buffer is present at the front of the queue, POLLERR is returned. - */ -unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, -				       struct file *file, poll_table *wait) -{ -	struct isp_video_buffer *buf; -	unsigned int mask = 0; - -	mutex_lock(&queue->lock); -	if (list_empty(&queue->queue)) { -		mask |= POLLERR; -		goto done; -	} -	buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); - -	poll_wait(file, &buf->wait, wait); -	if (buf->state == ISP_BUF_STATE_DONE || -	    buf->state == ISP_BUF_STATE_ERROR) { -		if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) -			mask |= POLLIN | POLLRDNORM; -		else -			mask |= POLLOUT | POLLWRNORM; -	} - -done: -	mutex_unlock(&queue->lock); -	return mask; -} diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h deleted file mode 100644 index 3e048ad6564..00000000000 --- a/drivers/media/platform/omap3isp/ispqueue.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * ispqueue.h - * - * TI OMAP3 ISP - Video buffers queue handling - * - * Copyright (C) 2010 Nokia Corporation - * - * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> - *	     Sakari Ailus <sakari.ailus@iki.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef OMAP3_ISP_QUEUE_H -#define OMAP3_ISP_QUEUE_H - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/mm_types.h> -#include <linux/mutex.h> -#include <linux/videodev2.h> -#include <linux/wait.h> - -struct isp_video_queue; -struct page; -struct scatterlist; - -#define ISP_VIDEO_MAX_BUFFERS		16 - -/** - * enum isp_video_buffer_state - ISP video buffer state - * @ISP_BUF_STATE_IDLE:	The buffer is under userspace control (dequeued - *	or not queued yet). - * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the - *	device yet. - * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer. - * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error - *	occurred. For capture device the buffer likely contains corrupted data or - *	no data at all. - * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occurred. - *	For capture devices the buffer contains valid data. - */ -enum isp_video_buffer_state { -	ISP_BUF_STATE_IDLE, -	ISP_BUF_STATE_QUEUED, -	ISP_BUF_STATE_ACTIVE, -	ISP_BUF_STATE_ERROR, -	ISP_BUF_STATE_DONE, -}; - -/** - * struct isp_video_buffer - ISP video buffer - * @vma_use_count: Number of times the buffer is mmap'ed to userspace - * @stream: List head for insertion into main queue - * @queue: ISP buffers queue this buffer belongs to - * @prepared: Whether the buffer has been prepared - * @skip_cache: Whether to skip cache management operations for this buffer - * @vaddr: Memory virtual address (for kernel buffers) - * @vm_flags: Buffer VMA flags (for userspace buffers) - * @offset: Offset inside the first page (for userspace buffers) - * @npages: Number of pages (for userspace buffers) - * @pages: Pages table (for userspace non-VM_PFNMAP buffers) - * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) - * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers) - * @sglist: Scatter list (for non-VM_PFNMAP buffers) - * @vbuf: V4L2 buffer - * @irqlist: List head for insertion into IRQ queue - * @state: Current buffer state - * @wait: Wait queue to signal buffer completion - */ -struct isp_video_buffer { -	unsigned long vma_use_count; -	struct list_head stream; -	struct isp_video_queue *queue; -	unsigned int prepared:1; -	bool skip_cache; - -	/* For kernel buffers. */ -	void *vaddr; - -	/* For userspace buffers. */ -	vm_flags_t vm_flags; -	unsigned long offset; -	unsigned int npages; -	struct page **pages; -	dma_addr_t paddr; - -	/* For all buffers except VM_PFNMAP. */ -	unsigned int sglen; -	struct scatterlist *sglist; - -	/* Touched by the interrupt handler. */ -	struct v4l2_buffer vbuf; -	struct list_head irqlist; -	enum isp_video_buffer_state state; -	wait_queue_head_t wait; -}; - -#define to_isp_video_buffer(vb)	container_of(vb, struct isp_video_buffer, vb) - -/** - * struct isp_video_queue_operations - Driver-specific operations - * @queue_prepare: Called before allocating buffers. Drivers should clamp the - *	number of buffers according to their requirements, and must return the - *	buffer size in bytes. - * @buffer_prepare: Called the first time a buffer is queued, or after changing - *	the userspace memory address for a USERPTR buffer, with the queue lock - *	held. Drivers should perform device-specific buffer preparation (such as - *	mapping the buffer memory in an IOMMU). This operation is optional. - * @buffer_queue: Called when a buffer is being added to the queue with the - *	queue irqlock spinlock held. - * @buffer_cleanup: Called before freeing buffers, or before changing the - *	userspace memory address for a USERPTR buffer, with the queue lock held. - *	Drivers must perform cleanup operations required to undo the - *	buffer_prepare call. This operation is optional. - */ -struct isp_video_queue_operations { -	void (*queue_prepare)(struct isp_video_queue *queue, -			      unsigned int *nbuffers, unsigned int *size); -	int  (*buffer_prepare)(struct isp_video_buffer *buf); -	void (*buffer_queue)(struct isp_video_buffer *buf); -	void (*buffer_cleanup)(struct isp_video_buffer *buf); -}; - -/** - * struct isp_video_queue - ISP video buffers queue - * @type: Type of video buffers handled by this queue - * @ops: Queue operations - * @dev: Device used for DMA operations - * @bufsize: Size of a driver-specific buffer object - * @count: Number of currently allocated buffers - * @buffers: ISP video buffers - * @lock: Mutex to protect access to the buffers, main queue and state - * @irqlock: Spinlock to protect access to the IRQ queue - * @streaming: Queue state, indicates whether the queue is streaming - * @queue: List of all queued buffers - */ -struct isp_video_queue { -	enum v4l2_buf_type type; -	const struct isp_video_queue_operations *ops; -	struct device *dev; -	unsigned int bufsize; - -	unsigned int count; -	struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS]; -	struct mutex lock; -	spinlock_t irqlock; - -	unsigned int streaming:1; - -	struct list_head queue; -}; - -int omap3isp_video_queue_cleanup(struct isp_video_queue *queue); -int omap3isp_video_queue_init(struct isp_video_queue *queue, -			      enum v4l2_buf_type type, -			      const struct isp_video_queue_operations *ops, -			      struct device *dev, unsigned int bufsize); - -int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, -				 struct v4l2_requestbuffers *rb); -int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, -				  struct v4l2_buffer *vbuf); -int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, -			      struct v4l2_buffer *vbuf); -int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, -			       struct v4l2_buffer *vbuf, int nonblocking); -int omap3isp_video_queue_streamon(struct isp_video_queue *queue); -void omap3isp_video_queue_streamoff(struct isp_video_queue *queue); -void omap3isp_video_queue_discard_done(struct isp_video_queue *queue); -int omap3isp_video_queue_mmap(struct isp_video_queue *queue, -			      struct vm_area_struct *vma); -unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, -				       struct file *file, poll_table *wait); - -#endif /* OMAP3_ISP_QUEUE_H */ diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c index d11fb261d53..6f077c2377d 100644 --- a/drivers/media/platform/omap3isp/ispresizer.c +++ b/drivers/media/platform/omap3isp/ispresizer.c @@ -206,7 +206,7 @@ static void resizer_set_bilinear(struct isp_res_device *res,  /*   * resizer_set_ycpos - Luminance and chrominance order   * @res: Device context. - * @order: order type. + * @pixelcode: pixel code.   */  static void resizer_set_ycpos(struct isp_res_device *res,  			      enum v4l2_mbus_pixelcode pixelcode) @@ -918,8 +918,8 @@ static void resizer_calc_ratios(struct isp_res_device *res,  /*   * resizer_set_crop_params - Setup hardware with cropping parameters   * @res : resizer private structure - * @crop_rect : current crop rectangle - * @ratio : resizer ratios + * @input : format on sink pad + * @output : format on source pad   * return none   */  static void resizer_set_crop_params(struct isp_res_device *res, @@ -1040,7 +1040,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)  	 */  	buffer = omap3isp_video_buffer_next(&res->video_out);  	if (buffer != NULL) { -		resizer_set_outaddr(res, buffer->isp_addr); +		resizer_set_outaddr(res, buffer->dma);  		restart = 1;  	} @@ -1049,7 +1049,7 @@ static void resizer_isr_buffer(struct isp_res_device *res)  	if (res->input == RESIZER_INPUT_MEMORY) {  		buffer = omap3isp_video_buffer_next(&res->video_in);  		if (buffer != NULL) -			resizer_set_inaddr(res, buffer->isp_addr); +			resizer_set_inaddr(res, buffer->dma);  		pipe->state |= ISP_PIPELINE_IDLE_INPUT;  	} @@ -1101,7 +1101,7 @@ static int resizer_video_queue(struct isp_video *video,  	struct isp_res_device *res = &video->isp->isp_res;  	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) -		resizer_set_inaddr(res, buffer->isp_addr); +		resizer_set_inaddr(res, buffer->dma);  	/*  	 * We now have a buffer queued on the output. Despite what the @@ -1116,7 +1116,7 @@ static int resizer_video_queue(struct isp_video *video,  	 * continuous mode or when starting the stream.  	 */  	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) -		resizer_set_outaddr(res, buffer->isp_addr); +		resizer_set_outaddr(res, buffer->dma);  	return 0;  } @@ -1532,6 +1532,20 @@ static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,  	return 0;  } +static int resizer_link_validate(struct v4l2_subdev *sd, +				 struct media_link *link, +				 struct v4l2_subdev_format *source_fmt, +				 struct v4l2_subdev_format *sink_fmt) +{ +	struct isp_res_device *res = v4l2_get_subdevdata(sd); +	struct isp_pipeline *pipe = to_isp_pipeline(&sd->entity); + +	omap3isp_resizer_max_rate(res, &pipe->max_rate); + +	return v4l2_subdev_link_validate_default(sd, link, +						 source_fmt, sink_fmt); +} +  /*   * resizer_init_formats - Initialize formats on all pads   * @sd: ISP resizer V4L2 subdevice @@ -1570,6 +1584,7 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {  	.set_fmt = resizer_set_format,  	.get_selection = resizer_get_selection,  	.set_selection = resizer_set_selection, +	.link_validate = resizer_link_validate,  };  /* subdev operations */ @@ -1701,7 +1716,8 @@ static int resizer_init_entities(struct isp_res_device *res)  	v4l2_set_subdevdata(sd, res);  	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -	pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +	pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK +				    | MEDIA_PAD_FL_MUST_CONNECT;  	pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;  	me->ops = &resizer_media_ops; diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h index 70c1c0e1bbd..9b01e9047c1 100644 --- a/drivers/media/platform/omap3isp/ispresizer.h +++ b/drivers/media/platform/omap3isp/ispresizer.h @@ -30,12 +30,12 @@  #include <linux/types.h>  /* - * Constants for filter coefficents count + * Constants for filter coefficients count   */  #define COEFF_CNT		32  /* - * struct isprsz_coef - Structure for resizer filter coeffcients. + * struct isprsz_coef - Structure for resizer filter coefficients.   * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap   *			mode (.5x-4x)   * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 61e17f9bd8b..e6cbc1eaf4c 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -26,13 +26,12 @@   */  #include <linux/dma-mapping.h> -#include <linux/omap-iommu.h>  #include <linux/slab.h>  #include <linux/uaccess.h>  #include "isp.h" -#define IS_COHERENT_BUF(stat)	((stat)->dma_ch >= 0) +#define ISP_STAT_USES_DMAENGINE(stat)	((stat)->dma_ch >= 0)  /*   * MAGIC_SIZE must always be the greatest common divisor of @@ -77,21 +76,10 @@ static void __isp_stat_buf_sync_magic(struct ispstat *stat,  					dma_addr_t, unsigned long, size_t,  					enum dma_data_direction))  { -	struct device *dev = stat->isp->dev; -	struct page *pg; -	dma_addr_t dma_addr; -	u32 offset; - -	/* Initial magic words */ -	pg = vmalloc_to_page(buf->virt_addr); -	dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); -	dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir); - -	/* Final magic words */ -	pg = vmalloc_to_page(buf->virt_addr + buf_size); -	dma_addr = pfn_to_dma(dev, page_to_pfn(pg)); -	offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK; -	dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir); +	/* Sync the initial and final magic words. */ +	dma_sync(stat->isp->dev, buf->dma_addr, 0, MAGIC_SIZE, dir); +	dma_sync(stat->isp->dev, buf->dma_addr + (buf_size & PAGE_MASK), +		 buf_size & ~PAGE_MASK, MAGIC_SIZE, dir);  }  static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat, @@ -99,7 +87,7 @@ static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,  					       u32 buf_size,  					       enum dma_data_direction dir)  { -	if (IS_COHERENT_BUF(stat)) +	if (ISP_STAT_USES_DMAENGINE(stat))  		return;  	__isp_stat_buf_sync_magic(stat, buf, buf_size, dir, @@ -111,7 +99,7 @@ static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,  					    u32 buf_size,  					    enum dma_data_direction dir)  { -	if (IS_COHERENT_BUF(stat)) +	if (ISP_STAT_USES_DMAENGINE(stat))  		return;  	__isp_stat_buf_sync_magic(stat, buf, buf_size, dir, @@ -144,7 +132,7 @@ static int isp_stat_buf_check_magic(struct ispstat *stat,  	for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;  	     w < end; w++) {  		if (unlikely(*w != MAGIC_NUM)) { -			dev_dbg(stat->isp->dev, "%s: endding magic check does " +			dev_dbg(stat->isp->dev, "%s: ending magic check does "  				"not match.\n", stat->subdev.name);  			return -EINVAL;  		} @@ -180,21 +168,21 @@ static void isp_stat_buf_insert_magic(struct ispstat *stat,  static void isp_stat_buf_sync_for_device(struct ispstat *stat,  					 struct ispstat_buffer *buf)  { -	if (IS_COHERENT_BUF(stat)) +	if (ISP_STAT_USES_DMAENGINE(stat))  		return; -	dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl, -			       buf->iovm->sgt->nents, DMA_FROM_DEVICE); +	dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl, +			       buf->sgt.nents, DMA_FROM_DEVICE);  }  static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,  				      struct ispstat_buffer *buf)  { -	if (IS_COHERENT_BUF(stat)) +	if (ISP_STAT_USES_DMAENGINE(stat))  		return; -	dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl, -			    buf->iovm->sgt->nents, DMA_FROM_DEVICE); +	dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl, +			    buf->sgt.nents, DMA_FROM_DEVICE);  }  static void isp_stat_buf_clear(struct ispstat *stat) @@ -354,29 +342,21 @@ static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,  static void isp_stat_bufs_free(struct ispstat *stat)  { -	struct isp_device *isp = stat->isp; -	int i; +	struct device *dev = ISP_STAT_USES_DMAENGINE(stat) +			   ? NULL : stat->isp->dev; +	unsigned int i;  	for (i = 0; i < STAT_MAX_BUFS; i++) {  		struct ispstat_buffer *buf = &stat->buf[i]; -		if (!IS_COHERENT_BUF(stat)) { -			if (IS_ERR_OR_NULL((void *)buf->iommu_addr)) -				continue; -			if (buf->iovm) -				dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, -					     buf->iovm->sgt->nents, -					     DMA_FROM_DEVICE); -			omap_iommu_vfree(isp->domain, isp->dev, -							buf->iommu_addr); -		} else { -			if (!buf->virt_addr) -				continue; -			dma_free_coherent(stat->isp->dev, stat->buf_alloc_size, -					  buf->virt_addr, buf->dma_addr); -		} -		buf->iommu_addr = 0; -		buf->iovm = NULL; +		if (!buf->virt_addr) +			continue; + +		sg_free_table(&buf->sgt); + +		dma_free_coherent(dev, stat->buf_alloc_size, buf->virt_addr, +				  buf->dma_addr); +  		buf->dma_addr = 0;  		buf->virt_addr = NULL;  		buf->empty = 1; @@ -389,83 +369,51 @@ static void isp_stat_bufs_free(struct ispstat *stat)  	stat->active_buf = NULL;  } -static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) -{ -	struct isp_device *isp = stat->isp; -	int i; - -	stat->buf_alloc_size = size; - -	for (i = 0; i < STAT_MAX_BUFS; i++) { -		struct ispstat_buffer *buf = &stat->buf[i]; -		struct iovm_struct *iovm; - -		WARN_ON(buf->dma_addr); -		buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0, -							size, IOMMU_FLAG); -		if (IS_ERR((void *)buf->iommu_addr)) { -			dev_err(stat->isp->dev, -				 "%s: Can't acquire memory for " -				 "buffer %d\n", stat->subdev.name, i); -			isp_stat_bufs_free(stat); -			return -ENOMEM; -		} - -		iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr); -		if (!iovm || -		    !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, -				DMA_FROM_DEVICE)) { -			isp_stat_bufs_free(stat); -			return -ENOMEM; -		} -		buf->iovm = iovm; - -		buf->virt_addr = omap_da_to_va(stat->isp->dev, -					  (u32)buf->iommu_addr); -		buf->empty = 1; -		dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." -			"iommu_addr=0x%08lx virt_addr=0x%08lx", -			stat->subdev.name, i, buf->iommu_addr, -			(unsigned long)buf->virt_addr); -	} - -	return 0; -} - -static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size) +static int isp_stat_bufs_alloc_one(struct device *dev, +				   struct ispstat_buffer *buf, +				   unsigned int size)  { -	int i; - -	stat->buf_alloc_size = size; - -	for (i = 0; i < STAT_MAX_BUFS; i++) { -		struct ispstat_buffer *buf = &stat->buf[i]; - -		WARN_ON(buf->iommu_addr); -		buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size, -					&buf->dma_addr, GFP_KERNEL | GFP_DMA); +	int ret; -		if (!buf->virt_addr || !buf->dma_addr) { -			dev_info(stat->isp->dev, -				 "%s: Can't acquire memory for " -				 "DMA buffer %d\n", stat->subdev.name, i); -			isp_stat_bufs_free(stat); -			return -ENOMEM; -		} -		buf->empty = 1; +	buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr, +					    GFP_KERNEL | GFP_DMA); +	if (!buf->virt_addr) +		return -ENOMEM; -		dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." -			"dma_addr=0x%08lx virt_addr=0x%08lx\n", -			stat->subdev.name, i, (unsigned long)buf->dma_addr, -			(unsigned long)buf->virt_addr); +	ret = dma_get_sgtable(dev, &buf->sgt, buf->virt_addr, buf->dma_addr, +			      size); +	if (ret < 0) { +		dma_free_coherent(dev, size, buf->virt_addr, buf->dma_addr); +		buf->virt_addr = NULL; +		buf->dma_addr = 0; +		return ret;  	}  	return 0;  } +/* + * The device passed to the DMA API depends on whether the statistics block uses + * ISP DMA, external DMA or PIO to transfer data. + * + * The first case (for the AEWB and AF engines) passes the ISP device, resulting + * in the DMA buffers being mapped through the ISP IOMMU. + * + * The second case (for the histogram engine) should pass the DMA engine device. + * As that device isn't accessible through the OMAP DMA engine API the driver + * passes NULL instead, resulting in the buffers being mapped directly as + * physical pages. + * + * The third case (for the histogram engine) doesn't require any mapping. The + * buffers could be allocated with kmalloc/vmalloc, but we still use + * dma_alloc_coherent() for consistency purpose. + */  static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)  { +	struct device *dev = ISP_STAT_USES_DMAENGINE(stat) +			   ? NULL : stat->isp->dev;  	unsigned long flags; +	unsigned int i;  	spin_lock_irqsave(&stat->isp->stat_lock, flags); @@ -489,10 +437,31 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)  	isp_stat_bufs_free(stat); -	if (IS_COHERENT_BUF(stat)) -		return isp_stat_bufs_alloc_dma(stat, size); -	else -		return isp_stat_bufs_alloc_iommu(stat, size); +	stat->buf_alloc_size = size; + +	for (i = 0; i < STAT_MAX_BUFS; i++) { +		struct ispstat_buffer *buf = &stat->buf[i]; +		int ret; + +		ret = isp_stat_bufs_alloc_one(dev, buf, size); +		if (ret < 0) { +			dev_err(stat->isp->dev, +				"%s: Failed to allocate DMA buffer %u\n", +				stat->subdev.name, i); +			isp_stat_bufs_free(stat); +			return ret; +		} + +		buf->empty = 1; + +		dev_dbg(stat->isp->dev, +			"%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx", +			stat->subdev.name, i, +			(unsigned long)buf->dma_addr, +			(unsigned long)buf->virt_addr); +	} + +	return 0;  }  static void isp_stat_queue_event(struct ispstat *stat, int err) @@ -841,7 +810,7 @@ int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)  	if (enable) {  		/*  		 * Only set enable PCR bit if the module was previously -		 * enabled through ioct. +		 * enabled through ioctl.  		 */  		isp_stat_try_enable(stat);  	} else { @@ -1067,7 +1036,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name,  	subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;  	v4l2_set_subdevdata(subdev, stat); -	stat->pad.flags = MEDIA_PAD_FL_SINK; +	stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;  	me->ops = NULL;  	return media_entity_init(me, 1, &stat->pad, 0); diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index 9a047c929b9..58d6ac7cb66 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -46,8 +46,7 @@  struct ispstat;  struct ispstat_buffer { -	unsigned long iommu_addr; -	struct iovm_struct *iovm; +	struct sg_table sgt;  	void *virt_addr;  	dma_addr_t dma_addr;  	struct timespec ts; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index a908d006f52..e36bac26476 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -27,7 +27,6 @@  #include <linux/clk.h>  #include <linux/mm.h>  #include <linux/module.h> -#include <linux/omap-iommu.h>  #include <linux/pagemap.h>  #include <linux/scatterlist.h>  #include <linux/sched.h> @@ -35,6 +34,7 @@  #include <linux/vmalloc.h>  #include <media/v4l2-dev.h>  #include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h>  #include "ispvideo.h"  #include "isp.h" @@ -278,55 +278,6 @@ static int isp_video_get_graph_data(struct isp_video *video,  	return 0;  } -/* - * Validate a pipeline by checking both ends of all links for format - * discrepancies. - * - * Compute the minimum time per frame value as the maximum of time per frame - * limits reported by every block in the pipeline. - * - * Return 0 if all formats match, or -EPIPE if at least one link is found with - * different formats on its two ends or if the pipeline doesn't start with a - * video source (either a subdev with no input pad, or a non-subdev entity). - */ -static int isp_video_validate_pipeline(struct isp_pipeline *pipe) -{ -	struct isp_device *isp = pipe->output->isp; -	struct media_pad *pad; -	struct v4l2_subdev *subdev; - -	subdev = isp_video_remote_subdev(pipe->output, NULL); -	if (subdev == NULL) -		return -EPIPE; - -	while (1) { -		/* Retrieve the sink format */ -		pad = &subdev->entity.pads[0]; -		if (!(pad->flags & MEDIA_PAD_FL_SINK)) -			break; - -		/* Update the maximum frame rate */ -		if (subdev == &isp->isp_res.subdev) -			omap3isp_resizer_max_rate(&isp->isp_res, -						  &pipe->max_rate); - -		/* Retrieve the source format. Return an error if no source -		 * entity can be found, and stop checking the pipeline if the -		 * source entity isn't a subdev. -		 */ -		pad = media_entity_remote_pad(pad); -		if (pad == NULL) -			return -EPIPE; - -		if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) -			break; - -		subdev = media_entity_to_v4l2_subdev(pad->entity); -	} - -	return 0; -} -  static int  __isp_video_get_format(struct isp_video *video, struct v4l2_format *format)  { @@ -339,14 +290,11 @@ __isp_video_get_format(struct isp_video *video, struct v4l2_format *format)  	if (subdev == NULL)  		return -EINVAL; -	mutex_lock(&video->mutex); -  	fmt.pad = pad;  	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; -	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); -	if (ret == -ENOIOCTLCMD) -		ret = -EINVAL; +	mutex_lock(&video->mutex); +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);  	mutex_unlock(&video->mutex);  	if (ret) @@ -378,104 +326,56 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)  }  /* ----------------------------------------------------------------------------- - * IOMMU management - */ - -#define IOMMU_FLAG	(IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) - -/* - * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list - * @dev: Device pointer specific to the OMAP3 ISP. - * @sglist: Pointer to source Scatter gather list to allocate. - * @sglen: Number of elements of the scatter-gatter list. - * - * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if - * we ran out of memory. - */ -static dma_addr_t -ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) -{ -	struct sg_table *sgt; -	u32 da; - -	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); -	if (sgt == NULL) -		return -ENOMEM; - -	sgt->sgl = (struct scatterlist *)sglist; -	sgt->nents = sglen; -	sgt->orig_nents = sglen; - -	da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG); -	if (IS_ERR_VALUE(da)) -		kfree(sgt); - -	return da; -} - -/* - * ispmmu_vunmap - Unmap a device address from the ISP MMU - * @dev: Device pointer specific to the OMAP3 ISP. - * @da: Device address generated from a ispmmu_vmap call. - */ -static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) -{ -	struct sg_table *sgt; - -	sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da); -	kfree(sgt); -} - -/* -----------------------------------------------------------------------------   * Video queue operations   */ -static void isp_video_queue_prepare(struct isp_video_queue *queue, -				    unsigned int *nbuffers, unsigned int *size) +static int isp_video_queue_setup(struct vb2_queue *queue, +				 const struct v4l2_format *fmt, +				 unsigned int *count, unsigned int *num_planes, +				 unsigned int sizes[], void *alloc_ctxs[])  { -	struct isp_video_fh *vfh = -		container_of(queue, struct isp_video_fh, queue); +	struct isp_video_fh *vfh = vb2_get_drv_priv(queue);  	struct isp_video *video = vfh->video; -	*size = vfh->format.fmt.pix.sizeimage; -	if (*size == 0) -		return; +	*num_planes = 1; -	*nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); -} +	sizes[0] = vfh->format.fmt.pix.sizeimage; +	if (sizes[0] == 0) +		return -EINVAL; -static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) -{ -	struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); -	struct isp_buffer *buffer = to_isp_buffer(buf); -	struct isp_video *video = vfh->video; +	alloc_ctxs[0] = video->alloc_ctx; -	if (buffer->isp_addr) { -		ispmmu_vunmap(video->isp, buffer->isp_addr); -		buffer->isp_addr = 0; -	} +	*count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); + +	return 0;  } -static int isp_video_buffer_prepare(struct isp_video_buffer *buf) +static int isp_video_buffer_prepare(struct vb2_buffer *buf)  { -	struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); +	struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);  	struct isp_buffer *buffer = to_isp_buffer(buf);  	struct isp_video *video = vfh->video; -	unsigned long addr; +	dma_addr_t addr; -	addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); -	if (IS_ERR_VALUE(addr)) +	/* Refuse to prepare the buffer is the video node has registered an +	 * error. We don't need to take any lock here as the operation is +	 * inherently racy. The authoritative check will be performed in the +	 * queue handler, which can't return an error, this check is just a best +	 * effort to notify userspace as early as possible. +	 */ +	if (unlikely(video->error))  		return -EIO; +	addr = vb2_dma_contig_plane_dma_addr(buf, 0);  	if (!IS_ALIGNED(addr, 32)) { -		dev_dbg(video->isp->dev, "Buffer address must be " -			"aligned to 32 bytes boundary.\n"); -		ispmmu_vunmap(video->isp, buffer->isp_addr); +		dev_dbg(video->isp->dev, +			"Buffer address must be aligned to 32 bytes boundary.\n");  		return -EINVAL;  	} -	buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; -	buffer->isp_addr = addr; +	vb2_set_plane_payload(&buffer->vb, 0, vfh->format.fmt.pix.sizeimage); +	buffer->dma = addr; +  	return 0;  } @@ -488,9 +388,9 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)   * If the pipeline is busy, it will be restarted in the output module interrupt   * handler.   */ -static void isp_video_buffer_queue(struct isp_video_buffer *buf) +static void isp_video_buffer_queue(struct vb2_buffer *buf)  { -	struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); +	struct isp_video_fh *vfh = vb2_get_drv_priv(buf->vb2_queue);  	struct isp_buffer *buffer = to_isp_buffer(buf);  	struct isp_video *video = vfh->video;  	struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); @@ -499,8 +399,18 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)  	unsigned int empty;  	unsigned int start; +	spin_lock_irqsave(&video->irqlock, flags); + +	if (unlikely(video->error)) { +		vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR); +		spin_unlock_irqrestore(&video->irqlock, flags); +		return; +	} +  	empty = list_empty(&video->dmaqueue); -	list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); +	list_add_tail(&buffer->irqlist, &video->dmaqueue); + +	spin_unlock_irqrestore(&video->irqlock, flags);  	if (empty) {  		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -524,23 +434,22 @@ static void isp_video_buffer_queue(struct isp_video_buffer *buf)  	}  } -static const struct isp_video_queue_operations isp_video_queue_ops = { -	.queue_prepare = &isp_video_queue_prepare, -	.buffer_prepare = &isp_video_buffer_prepare, -	.buffer_queue = &isp_video_buffer_queue, -	.buffer_cleanup = &isp_video_buffer_cleanup, +static const struct vb2_ops isp_video_queue_ops = { +	.queue_setup = isp_video_queue_setup, +	.buf_prepare = isp_video_buffer_prepare, +	.buf_queue = isp_video_buffer_queue,  };  /*   * omap3isp_video_buffer_next - Complete the current buffer and return the next   * @video: ISP video object   * - * Remove the current video buffer from the DMA queue and fill its timestamp, - * field count and state fields before waking up its completion handler. + * Remove the current video buffer from the DMA queue and fill its timestamp and + * field count before handing it back to videobuf2.   * - * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no - * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise. - * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE. + * For capture video nodes the buffer state is set to VB2_BUF_STATE_DONE if no + * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. + * For video output nodes the buffer state is always set to VB2_BUF_STATE_DONE.   *   * The DMA queue is expected to contain at least one buffer.   * @@ -550,26 +459,25 @@ static const struct isp_video_queue_operations isp_video_queue_ops = {  struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)  {  	struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); -	struct isp_video_queue *queue = video->queue;  	enum isp_pipeline_state state; -	struct isp_video_buffer *buf; +	struct isp_buffer *buf;  	unsigned long flags;  	struct timespec ts; -	spin_lock_irqsave(&queue->irqlock, flags); +	spin_lock_irqsave(&video->irqlock, flags);  	if (WARN_ON(list_empty(&video->dmaqueue))) { -		spin_unlock_irqrestore(&queue->irqlock, flags); +		spin_unlock_irqrestore(&video->irqlock, flags);  		return NULL;  	} -	buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, +	buf = list_first_entry(&video->dmaqueue, struct isp_buffer,  			       irqlist);  	list_del(&buf->irqlist); -	spin_unlock_irqrestore(&queue->irqlock, flags); +	spin_unlock_irqrestore(&video->irqlock, flags);  	ktime_get_ts(&ts); -	buf->vbuf.timestamp.tv_sec = ts.tv_sec; -	buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; +	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec; +	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;  	/* Do frame number propagation only if this is the output video node.  	 * Frame number either comes from the CSI receivers or it gets @@ -578,22 +486,27 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)  	 * first, so the input number might lag behind by 1 in some cases.  	 */  	if (video == pipe->output && !pipe->do_propagation) -		buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); +		buf->vb.v4l2_buf.sequence = +			atomic_inc_return(&pipe->frame_number);  	else -		buf->vbuf.sequence = atomic_read(&pipe->frame_number); +		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);  	/* Report pipeline errors to userspace on the capture device side. */ -	if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { -		buf->state = ISP_BUF_STATE_ERROR; +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) { +		state = VB2_BUF_STATE_ERROR;  		pipe->error = false;  	} else { -		buf->state = ISP_BUF_STATE_DONE; +		state = VB2_BUF_STATE_DONE;  	} -	wake_up(&buf->wait); +	vb2_buffer_done(&buf->vb, state); + +	spin_lock_irqsave(&video->irqlock, flags);  	if (list_empty(&video->dmaqueue)) { -		if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +		spin_unlock_irqrestore(&video->irqlock, flags); + +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)  			state = ISP_PIPELINE_QUEUE_OUTPUT  			      | ISP_PIPELINE_STREAM;  		else @@ -608,16 +521,46 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)  		return NULL;  	} -	if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { -		spin_lock_irqsave(&pipe->lock, flags); +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { +		spin_lock(&pipe->lock);  		pipe->state &= ~ISP_PIPELINE_STREAM; -		spin_unlock_irqrestore(&pipe->lock, flags); +		spin_unlock(&pipe->lock);  	} -	buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, +	buf = list_first_entry(&video->dmaqueue, struct isp_buffer,  			       irqlist); -	buf->state = ISP_BUF_STATE_ACTIVE; -	return to_isp_buffer(buf); +	buf->vb.state = VB2_BUF_STATE_ACTIVE; + +	spin_unlock_irqrestore(&video->irqlock, flags); + +	return buf; +} + +/* + * omap3isp_video_cancel_stream - Cancel stream on a video node + * @video: ISP video object + * + * Cancelling a stream mark all buffers on the video node as erroneous and makes + * sure no new buffer can be queued. + */ +void omap3isp_video_cancel_stream(struct isp_video *video) +{ +	unsigned long flags; + +	spin_lock_irqsave(&video->irqlock, flags); + +	while (!list_empty(&video->dmaqueue)) { +		struct isp_buffer *buf; + +		buf = list_first_entry(&video->dmaqueue, +				       struct isp_buffer, irqlist); +		list_del(&buf->irqlist); +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); +	} + +	video->error = true; + +	spin_unlock_irqrestore(&video->irqlock, flags);  }  /* @@ -634,12 +577,15 @@ void omap3isp_video_resume(struct isp_video *video, int continuous)  {  	struct isp_buffer *buf = NULL; -	if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) -		omap3isp_video_queue_discard_done(video->queue); +	if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { +		mutex_lock(&video->queue_lock); +		vb2_discard_done(video->queue); +		mutex_unlock(&video->queue_lock); +	}  	if (!list_empty(&video->dmaqueue)) {  		buf = list_first_entry(&video->dmaqueue, -				       struct isp_buffer, buffer.irqlist); +				       struct isp_buffer, irqlist);  		video->ops->queue(video, buf);  		video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;  	} else { @@ -847,33 +793,56 @@ static int  isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)  {  	struct isp_video_fh *vfh = to_isp_video_fh(fh); +	struct isp_video *video = video_drvdata(file); +	int ret; + +	mutex_lock(&video->queue_lock); +	ret = vb2_reqbufs(&vfh->queue, rb); +	mutex_unlock(&video->queue_lock); -	return omap3isp_video_queue_reqbufs(&vfh->queue, rb); +	return ret;  }  static int  isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)  {  	struct isp_video_fh *vfh = to_isp_video_fh(fh); +	struct isp_video *video = video_drvdata(file); +	int ret; + +	mutex_lock(&video->queue_lock); +	ret = vb2_querybuf(&vfh->queue, b); +	mutex_unlock(&video->queue_lock); -	return omap3isp_video_queue_querybuf(&vfh->queue, b); +	return ret;  }  static int  isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)  {  	struct isp_video_fh *vfh = to_isp_video_fh(fh); +	struct isp_video *video = video_drvdata(file); +	int ret; + +	mutex_lock(&video->queue_lock); +	ret = vb2_qbuf(&vfh->queue, b); +	mutex_unlock(&video->queue_lock); -	return omap3isp_video_queue_qbuf(&vfh->queue, b); +	return ret;  }  static int  isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)  {  	struct isp_video_fh *vfh = to_isp_video_fh(fh); +	struct isp_video *video = video_drvdata(file); +	int ret; + +	mutex_lock(&video->queue_lock); +	ret = vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); +	mutex_unlock(&video->queue_lock); -	return omap3isp_video_queue_dqbuf(&vfh->queue, b, -					  file->f_flags & O_NONBLOCK); +	return ret;  }  static int isp_video_check_external_subdevs(struct isp_video *video, @@ -893,7 +862,11 @@ static int isp_video_check_external_subdevs(struct isp_video *video,  	struct v4l2_ext_controls ctrls;  	struct v4l2_ext_control ctrl;  	unsigned int i; -	int ret = 0; +	int ret; + +	/* Memory-to-memory pipelines have no external subdev. */ +	if (pipe->input != NULL) +		return 0;  	for (i = 0; i < ARRAY_SIZE(ents); i++) {  		/* Is the entity part of the pipeline? */ @@ -912,7 +885,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,  	if (!source) {  		dev_warn(isp->dev, "can't find source, failing now\n"); -		return ret; +		return -EINVAL;  	}  	if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -1009,11 +982,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)  	mutex_lock(&video->stream_lock); -	if (video->streaming) { -		mutex_unlock(&video->stream_lock); -		return -EBUSY; -	} -  	/* Start streaming on the pipeline. No link touching an entity in the  	 * pipeline can be activated or deactivated once streaming is started.  	 */ @@ -1054,11 +1022,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)  	if (ret < 0)  		goto err_check_format; -	/* Validate the pipeline and update its state. */ -	ret = isp_video_validate_pipeline(pipe); -	if (ret < 0) -		goto err_check_format; -  	pipe->error = false;  	spin_lock_irqsave(&pipe->lock, flags); @@ -1077,7 +1040,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)  	INIT_LIST_HEAD(&video->dmaqueue);  	atomic_set(&pipe->frame_number, -1); -	ret = omap3isp_video_queue_streamon(&vfh->queue); +	mutex_lock(&video->queue_lock); +	ret = vb2_streamon(&vfh->queue, type); +	mutex_unlock(&video->queue_lock);  	if (ret < 0)  		goto err_check_format; @@ -1090,19 +1055,19 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)  					      ISP_PIPELINE_STREAM_CONTINUOUS);  		if (ret < 0)  			goto err_set_stream; -		spin_lock_irqsave(&video->queue->irqlock, flags); +		spin_lock_irqsave(&video->irqlock, flags);  		if (list_empty(&video->dmaqueue))  			video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; -		spin_unlock_irqrestore(&video->queue->irqlock, flags); +		spin_unlock_irqrestore(&video->irqlock, flags);  	} -	video->streaming = 1; -  	mutex_unlock(&video->stream_lock);  	return 0;  err_set_stream: -	omap3isp_video_queue_streamoff(&vfh->queue); +	mutex_lock(&video->queue_lock); +	vb2_streamoff(&vfh->queue, type); +	mutex_unlock(&video->queue_lock);  err_check_format:  	media_entity_pipeline_stop(&video->video.entity);  err_pipeline_start: @@ -1138,9 +1103,9 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)  	mutex_lock(&video->stream_lock);  	/* Make sure we're not streaming yet. */ -	mutex_lock(&vfh->queue.lock); -	streaming = vfh->queue.streaming; -	mutex_unlock(&vfh->queue.lock); +	mutex_lock(&video->queue_lock); +	streaming = vb2_is_streaming(&vfh->queue); +	mutex_unlock(&video->queue_lock);  	if (!streaming)  		goto done; @@ -1159,9 +1124,13 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)  	/* Stop the stream. */  	omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); -	omap3isp_video_queue_streamoff(&vfh->queue); +	omap3isp_video_cancel_stream(video); + +	mutex_lock(&video->queue_lock); +	vb2_streamoff(&vfh->queue, type); +	mutex_unlock(&video->queue_lock);  	video->queue = NULL; -	video->streaming = 0; +	video->error = false;  	if (video->isp->pdata->set_constraints)  		video->isp->pdata->set_constraints(video->isp, false); @@ -1230,6 +1199,7 @@ static int isp_video_open(struct file *file)  {  	struct isp_video *video = video_drvdata(file);  	struct isp_video_fh *handle; +	struct vb2_queue *queue;  	int ret = 0;  	handle = kzalloc(sizeof(*handle), GFP_KERNEL); @@ -1251,9 +1221,20 @@ static int isp_video_open(struct file *file)  		goto done;  	} -	omap3isp_video_queue_init(&handle->queue, video->type, -				  &isp_video_queue_ops, video->isp->dev, -				  sizeof(struct isp_buffer)); +	queue = &handle->queue; +	queue->type = video->type; +	queue->io_modes = VB2_MMAP | VB2_USERPTR; +	queue->drv_priv = handle; +	queue->ops = &isp_video_queue_ops; +	queue->mem_ops = &vb2_dma_contig_memops; +	queue->buf_struct_size = sizeof(struct isp_buffer); +	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + +	ret = vb2_queue_init(&handle->queue); +	if (ret < 0) { +		omap3isp_put(video->isp); +		goto done; +	}  	memset(&handle->format, 0, sizeof(handle->format));  	handle->format.type = video->type; @@ -1280,9 +1261,9 @@ static int isp_video_release(struct file *file)  	/* Disable streaming and free the buffers queue resources. */  	isp_video_streamoff(file, vfh, video->type); -	mutex_lock(&handle->queue.lock); -	omap3isp_video_queue_cleanup(&handle->queue); -	mutex_unlock(&handle->queue.lock); +	mutex_lock(&video->queue_lock); +	vb2_queue_release(&handle->queue); +	mutex_unlock(&video->queue_lock);  	omap3isp_pipeline_pm_use(&video->video.entity, 0); @@ -1299,16 +1280,27 @@ static int isp_video_release(struct file *file)  static unsigned int isp_video_poll(struct file *file, poll_table *wait)  {  	struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); -	struct isp_video_queue *queue = &vfh->queue; +	struct isp_video *video = video_drvdata(file); +	int ret; -	return omap3isp_video_queue_poll(queue, file, wait); +	mutex_lock(&video->queue_lock); +	ret = vb2_poll(&vfh->queue, file, wait); +	mutex_unlock(&video->queue_lock); + +	return ret;  }  static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)  {  	struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); +	struct isp_video *video = video_drvdata(file); +	int ret; -	return omap3isp_video_queue_mmap(&vfh->queue, vma); +	mutex_lock(&video->queue_lock); +	ret = vb2_mmap(&vfh->queue, vma); +	mutex_unlock(&video->queue_lock); + +	return ret;  }  static struct v4l2_file_operations isp_video_fops = { @@ -1335,11 +1327,13 @@ int omap3isp_video_init(struct isp_video *video, const char *name)  	switch (video->type) {  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:  		direction = "output"; -		video->pad.flags = MEDIA_PAD_FL_SINK; +		video->pad.flags = MEDIA_PAD_FL_SINK +				   | MEDIA_PAD_FL_MUST_CONNECT;  		break;  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:  		direction = "input"; -		video->pad.flags = MEDIA_PAD_FL_SOURCE; +		video->pad.flags = MEDIA_PAD_FL_SOURCE +				   | MEDIA_PAD_FL_MUST_CONNECT;  		video->video.vfl_dir = VFL_DIR_TX;  		break; @@ -1347,15 +1341,23 @@ int omap3isp_video_init(struct isp_video *video, const char *name)  		return -EINVAL;  	} +	video->alloc_ctx = vb2_dma_contig_init_ctx(video->isp->dev); +	if (IS_ERR(video->alloc_ctx)) +		return PTR_ERR(video->alloc_ctx); +  	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); -	if (ret < 0) +	if (ret < 0) { +		vb2_dma_contig_cleanup_ctx(video->alloc_ctx);  		return ret; +	}  	mutex_init(&video->mutex);  	atomic_set(&video->active, 0);  	spin_lock_init(&video->pipe.lock);  	mutex_init(&video->stream_lock); +	mutex_init(&video->queue_lock); +	spin_lock_init(&video->irqlock);  	/* Initialize the video device. */  	if (video->ops == NULL) @@ -1376,7 +1378,9 @@ int omap3isp_video_init(struct isp_video *video, const char *name)  void omap3isp_video_cleanup(struct isp_video *video)  { +	vb2_dma_contig_cleanup_ctx(video->alloc_ctx);  	media_entity_cleanup(&video->video.entity); +	mutex_destroy(&video->queue_lock);  	mutex_destroy(&video->stream_lock);  	mutex_destroy(&video->mutex);  } diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h index 1ad470ec2b9..7d2e82122ec 100644 --- a/drivers/media/platform/omap3isp/ispvideo.h +++ b/drivers/media/platform/omap3isp/ispvideo.h @@ -30,8 +30,7 @@  #include <media/media-entity.h>  #include <media/v4l2-dev.h>  #include <media/v4l2-fh.h> - -#include "ispqueue.h" +#include <media/videobuf2-core.h>  #define ISP_VIDEO_DRIVER_NAME		"ispvideo"  #define ISP_VIDEO_DRIVER_VERSION	"0.0.2" @@ -124,17 +123,19 @@ static inline int isp_pipeline_ready(struct isp_pipeline *pipe)  			       ISP_PIPELINE_IDLE_OUTPUT);  } -/* - * struct isp_buffer - ISP buffer - * @buffer: ISP video buffer - * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. +/** + * struct isp_buffer - ISP video buffer + * @vb: videobuf2 buffer + * @irqlist: List head for insertion into IRQ queue + * @dma: DMA address   */  struct isp_buffer { -	struct isp_video_buffer buffer; -	dma_addr_t isp_addr; +	struct vb2_buffer vb; +	struct list_head irqlist; +	dma_addr_t dma;  }; -#define to_isp_buffer(buf)	container_of(buf, struct isp_buffer, buffer) +#define to_isp_buffer(buf)	container_of(buf, struct isp_buffer, vb)  enum isp_video_dmaqueue_flags {  	/* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */ @@ -172,15 +173,16 @@ struct isp_video {  	unsigned int bpl_value;		/* bytes per line value */  	unsigned int bpl_padding;	/* padding at end of line */ -	/* Entity video node streaming */ -	unsigned int streaming:1; -  	/* Pipeline state */  	struct isp_pipeline pipe;  	struct mutex stream_lock;	/* pipeline and stream states */ +	bool error;  	/* Video buffers queue */ -	struct isp_video_queue *queue; +	void *alloc_ctx; +	struct vb2_queue *queue; +	struct mutex queue_lock;	/* protects the queue */ +	spinlock_t irqlock;		/* protects dmaqueue */  	struct list_head dmaqueue;  	enum isp_video_dmaqueue_flags dmaqueue_flags; @@ -192,7 +194,7 @@ struct isp_video {  struct isp_video_fh {  	struct v4l2_fh vfh;  	struct isp_video *video; -	struct isp_video_queue queue; +	struct vb2_queue queue;  	struct v4l2_format format;  	struct v4l2_fract timeperframe;  }; @@ -207,6 +209,7 @@ int omap3isp_video_register(struct isp_video *video,  			    struct v4l2_device *vdev);  void omap3isp_video_unregister(struct isp_video *video);  struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video); +void omap3isp_video_cancel_stream(struct isp_video *video);  void omap3isp_video_resume(struct isp_video *video, int continuous);  struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);  | 
