diff options
Diffstat (limited to 'drivers/media/platform')
152 files changed, 15025 insertions, 9655 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c7caf94621b..8108c698b54 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -36,7 +36,8 @@ source "drivers/media/platform/blackfin/Kconfig"  config VIDEO_SH_VOU  	tristate "SuperH VOU video output driver"  	depends on MEDIA_CAMERA_SUPPORT -	depends on VIDEO_DEV && ARCH_SHMOBILE && I2C +	depends on VIDEO_DEV && I2C && HAS_DMA +	depends on ARCH_SHMOBILE || COMPILE_TEST  	select VIDEOBUF_DMA_CONTIG  	help  	  Support for the Video Output Unit (VOU) on SuperH SoCs. @@ -55,7 +56,7 @@ config VIDEO_VIU  config VIDEO_TIMBERDALE  	tristate "Support for timberdale Video In/LogiWIN" -	depends on VIDEO_V4L2 && I2C && DMADEVICES +	depends on MFD_TIMBERDALE && VIDEO_V4L2 && I2C && DMADEVICES  	select DMA_ENGINE  	select TIMB_DMA  	select VIDEO_ADV7180 @@ -90,16 +91,11 @@ config VIDEO_M32R_AR_M64278  	  To compile this driver as a module, choose M here: the  	  module will be called arv. -config VIDEO_OMAP2 -	tristate "OMAP2 Camera Capture Interface driver" -	depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE -	select VIDEOBUF_DMA_SG -	---help--- -	  This is a v4l2 driver for the TI OMAP2 camera capture interface -  config VIDEO_OMAP3  	tristate "OMAP 3 Camera support" -	depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 +	select ARM_DMA_USE_IOMMU +	select OMAP_IOMMU  	---help---  	  Driver for an OMAP 3 camera controller. @@ -112,7 +108,7 @@ config VIDEO_OMAP3_DEBUG  config VIDEO_S3C_CAMIF  	tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"  	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API -	depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME +	depends on (ARCH_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME  	select VIDEOBUF2_DMA_CONTIG  	---help---  	  This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera @@ -143,6 +139,7 @@ if V4L_MEM2MEM_DRIVERS  config VIDEO_CODA  	tristate "Chips&Media Coda multi-standard codec IP"  	depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC +	select SRAM  	select VIDEOBUF2_DMA_CONTIG  	select V4L2_MEM2MEM_DEV  	---help--- @@ -212,7 +209,7 @@ config VIDEO_SH_VEU  config VIDEO_RENESAS_VSP1  	tristate "Renesas VSP1 Video Processing Engine" -	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API +	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA  	select VIDEOBUF2_DMA_CONTIG  	---help---  	  This is a V4L2 driver for the Renesas VSP1 video processing engine. @@ -220,6 +217,22 @@ config VIDEO_RENESAS_VSP1  	  To compile this driver as a module, choose M here: the module  	  will be called vsp1. +config VIDEO_TI_VPE +	tristate "TI VPE (Video Processing Engine) driver" +	depends on VIDEO_DEV && VIDEO_V4L2 && SOC_DRA7XX +	select VIDEOBUF2_DMA_CONTIG +	select V4L2_MEM2MEM_DEV +	default n +	---help--- +	  Support for the TI VPE(Video Processing Engine) block +	  found on DRA7XX SoC. + +config VIDEO_TI_VPE_DEBUG +	bool "VPE debug messages" +	depends on VIDEO_TI_VPE +	---help--- +	  Enable debug messages on VPE driver. +  endif # V4L_MEM2MEM_DRIVERS  menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4e4da482c52..e5269da9190 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -2,8 +2,6 @@  # Makefile for the video capture/playback device drivers.  # -omap2cam-objs	:=	omap24xxcam.o omap24xxcam-dma.o -  obj-$(CONFIG_VIDEO_VINO) += indycam.o  obj-$(CONFIG_VIDEO_VINO) += vino.o @@ -14,7 +12,6 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o  obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/  obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ -obj-$(CONFIG_VIDEO_OMAP2)		+= omap2cam.o  obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/  obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o @@ -22,6 +19,8 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o  obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o +obj-$(CONFIG_VIDEO_TI_VPE)		+= ti-vpe/ +  obj-$(CONFIG_VIDEO_MX2_EMMAPRP)		+= mx2_emmaprp.o  obj-$(CONFIG_VIDEO_CODA) 		+= coda.o diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c index e346d32d08c..e9410e41ae0 100644 --- a/drivers/media/platform/arv.c +++ b/drivers/media/platform/arv.c @@ -109,7 +109,7 @@ extern struct cpuinfo_m32r	boot_cpu_data;  struct ar {  	struct v4l2_device v4l2_dev;  	struct video_device vdev; -	unsigned int start_capture;	/* duaring capture in INT. mode. */ +	int start_capture;	/* duaring capture in INT. mode. */  #if USE_INT  	unsigned char *line_buff;	/* DMA line buffer */  #endif @@ -307,11 +307,11 @@ static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)  	/*  	 * Okay, kick AR LSI to invoke an interrupt  	 */ -	ar->start_capture = 0; +	ar->start_capture = -1;  	ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);  	local_irq_restore(flags);  	/* .... AR interrupts .... */ -	interruptible_sleep_on(&ar->wait); +	wait_event_interruptible(ar->wait, ar->start_capture == 0);  	if (signal_pending(current)) {  		printk(KERN_ERR "arv: interrupted while get frame data.\n");  		ret = -EINTR; diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 4c110597709..16e4b1c525c 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -422,20 +422,17 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)  		return ret;  	} -	INIT_COMPLETION(bcap_dev->comp); +	reinit_completion(&bcap_dev->comp);  	bcap_dev->stop = false;  	return 0;  } -static int bcap_stop_streaming(struct vb2_queue *vq) +static void bcap_stop_streaming(struct vb2_queue *vq)  {  	struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);  	struct ppi_if *ppi = bcap_dev->ppi;  	int ret; -	if (!vb2_is_streaming(vq)) -		return 0; -  	bcap_dev->stop = true;  	wait_for_completion(&bcap_dev->comp);  	ppi->ops->stop(ppi); @@ -452,7 +449,6 @@ static int bcap_stop_streaming(struct vb2_queue *vq)  		list_del(&bcap_dev->cur_frm->list);  		vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR);  	} -	return 0;  }  static struct vb2_ops bcap_video_qops = { @@ -635,7 +631,7 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std)  	if (vb2_is_busy(&bcap_dev->buffer_queue))  		return -EBUSY; -	ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, std); +	ret = v4l2_subdev_call(bcap_dev->sd, video, s_std, std);  	if (ret < 0)  		return ret; @@ -648,7 +644,9 @@ static int bcap_enum_dv_timings(struct file *file, void *priv,  {  	struct bcap_device *bcap_dev = video_drvdata(file); -	return v4l2_subdev_call(bcap_dev->sd, video, +	timings->pad = 0; + +	return v4l2_subdev_call(bcap_dev->sd, pad,  			enum_dv_timings, timings);  } @@ -997,7 +995,7 @@ static int bcap_probe(struct platform_device *pdev)  	q->buf_struct_size = sizeof(struct bcap_buffer);  	q->ops = &bcap_video_qops;  	q->mem_ops = &vb2_dma_contig_memops; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	ret = vb2_queue_init(q);  	if (ret) @@ -1069,7 +1067,7 @@ static int bcap_probe(struct platform_device *pdev)  	/* now we can probe the default state */  	if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) {  		v4l2_std_id std; -		ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); +		ret = v4l2_subdev_call(bcap_dev->sd, video, g_std, &std);  		if (ret) {  			v4l2_err(&bcap_dev->v4l2_dev,  					"Unable to get std\n"); diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 449d2fec9e8..b1783791d42 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -39,7 +39,7 @@  #define CODA_NAME		"coda" -#define CODA_MAX_INSTANCES	4 +#define CODADX6_MAX_INSTANCES	4  #define CODA_FMO_BUF_SIZE	32  #define CODADX6_WORK_BUF_SIZE	(288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) @@ -54,8 +54,6 @@  #define CODA_MAX_FRAMEBUFFERS	8 -#define MAX_W		8192 -#define MAX_H		8192  #define CODA_MAX_FRAME_SIZE	0x100000  #define FMO_SLICE_SAVE_BUF_SIZE         (32)  #define CODA_DEFAULT_GAMMA		4096 @@ -394,14 +392,57 @@ static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc,  	return &codecs[k];  } +static void coda_get_max_dimensions(struct coda_dev *dev, +				    struct coda_codec *codec, +				    int *max_w, int *max_h) +{ +	struct coda_codec *codecs = dev->devtype->codecs; +	int num_codecs = dev->devtype->num_codecs; +	unsigned int w, h; +	int k; + +	if (codec) { +		w = codec->max_w; +		h = codec->max_h; +	} else { +		for (k = 0, w = 0, h = 0; k < num_codecs; k++) { +			w = max(w, codecs[k].max_w); +			h = max(h, codecs[k].max_h); +		} +	} + +	if (max_w) +		*max_w = w; +	if (max_h) +		*max_h = h; +} + +static char *coda_product_name(int product) +{ +	static char buf[9]; + +	switch (product) { +	case CODA_DX6: +		return "CodaDx6"; +	case CODA_7541: +		return "CODA7541"; +	default: +		snprintf(buf, sizeof(buf), "(0x%04x)", product); +		return buf; +	} +} +  /*   * V4L2 ioctl() operations.   */ -static int vidioc_querycap(struct file *file, void *priv, -			   struct v4l2_capability *cap) +static int coda_querycap(struct file *file, void *priv, +			 struct v4l2_capability *cap)  { +	struct coda_ctx *ctx = fh_to_ctx(priv); +  	strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver)); -	strlcpy(cap->card, CODA_NAME, sizeof(cap->card)); +	strlcpy(cap->card, coda_product_name(ctx->dev->devtype->product), +		sizeof(cap->card));  	strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info));  	/*  	 * This is only a mem-to-mem video device. The capture and output @@ -457,6 +498,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,  		fmt = &formats[i];  		strlcpy(f->description, fmt->name, sizeof(f->description));  		f->pixelformat = fmt->fourcc; +		if (!coda_format_is_yuv(fmt->fourcc)) +			f->flags |= V4L2_FMT_FLAG_COMPRESSED;  		return 0;  	} @@ -464,8 +507,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,  	return -EINVAL;  } -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, -				   struct v4l2_fmtdesc *f) +static int coda_enum_fmt_vid_cap(struct file *file, void *priv, +				 struct v4l2_fmtdesc *f)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	struct vb2_queue *src_vq; @@ -483,13 +526,14 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,  	return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);  } -static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, -				   struct v4l2_fmtdesc *f) +static int coda_enum_fmt_vid_out(struct file *file, void *priv, +				 struct v4l2_fmtdesc *f)  {  	return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);  } -static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +static int coda_g_fmt(struct file *file, void *priv, +		      struct v4l2_format *f)  {  	struct vb2_queue *vq;  	struct coda_q_data *q_data; @@ -516,8 +560,11 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)  	return 0;  } -static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f) +static int coda_try_fmt(struct coda_ctx *ctx, struct coda_codec *codec, +			struct v4l2_format *f)  { +	struct coda_dev *dev = ctx->dev; +	struct coda_q_data *q_data;  	unsigned int max_w, max_h;  	enum v4l2_field field; @@ -531,32 +578,48 @@ static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f)  	 * if any of the dimensions is unsupported */  	f->fmt.pix.field = field; -	if (codec) { -		max_w = codec->max_w; -		max_h = codec->max_h; -	} else { -		max_w = MAX_W; -		max_h = MAX_H; +	coda_get_max_dimensions(dev, codec, &max_w, &max_h); +	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, W_ALIGN, +			      &f->fmt.pix.height, MIN_H, max_h, H_ALIGN, +			      S_ALIGN); + +	switch (f->fmt.pix.pixelformat) { +	case V4L2_PIX_FMT_YUV420: +	case V4L2_PIX_FMT_YVU420: +	case V4L2_PIX_FMT_H264: +	case V4L2_PIX_FMT_MPEG4: +	case V4L2_PIX_FMT_JPEG: +		break; +	default: +		q_data = get_q_data(ctx, f->type); +		f->fmt.pix.pixelformat = q_data->fourcc;  	} -	v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, -			      W_ALIGN, &f->fmt.pix.height, -			      MIN_H, max_h, H_ALIGN, S_ALIGN); -	if (coda_format_is_yuv(f->fmt.pix.pixelformat)) { +	switch (f->fmt.pix.pixelformat) { +	case V4L2_PIX_FMT_YUV420: +	case V4L2_PIX_FMT_YVU420:  		/* Frame stride must be multiple of 8 */  		f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8);  		f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *  					f->fmt.pix.height * 3 / 2; -	} else { /*encoded formats h.264/mpeg4 */ +		break; +	case V4L2_PIX_FMT_H264: +	case V4L2_PIX_FMT_MPEG4: +	case V4L2_PIX_FMT_JPEG:  		f->fmt.pix.bytesperline = 0;  		f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE; +		break; +	default: +		BUG();  	} +	f->fmt.pix.priv = 0; +  	return 0;  } -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, -				  struct v4l2_format *f) +static int coda_try_fmt_vid_cap(struct file *file, void *priv, +				struct v4l2_format *f)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	struct coda_codec *codec; @@ -584,7 +647,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,  	f->fmt.pix.colorspace = ctx->colorspace; -	ret = vidioc_try_fmt(codec, f); +	ret = coda_try_fmt(ctx, codec, f);  	if (ret < 0)  		return ret; @@ -600,8 +663,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,  	return 0;  } -static int vidioc_try_fmt_vid_out(struct file *file, void *priv, -				  struct v4l2_format *f) +static int coda_try_fmt_vid_out(struct file *file, void *priv, +				struct v4l2_format *f)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	struct coda_codec *codec; @@ -613,10 +676,10 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,  	if (!f->fmt.pix.colorspace)  		f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; -	return vidioc_try_fmt(codec, f); +	return coda_try_fmt(ctx, codec, f);  } -static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) +static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)  {  	struct coda_q_data *q_data;  	struct vb2_queue *vq; @@ -646,61 +709,62 @@ static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)  	return 0;  } -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, -				struct v4l2_format *f) +static int coda_s_fmt_vid_cap(struct file *file, void *priv, +			      struct v4l2_format *f)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	int ret; -	ret = vidioc_try_fmt_vid_cap(file, priv, f); +	ret = coda_try_fmt_vid_cap(file, priv, f);  	if (ret)  		return ret; -	return vidioc_s_fmt(ctx, f); +	return coda_s_fmt(ctx, f);  } -static int vidioc_s_fmt_vid_out(struct file *file, void *priv, -				struct v4l2_format *f) +static int coda_s_fmt_vid_out(struct file *file, void *priv, +			      struct v4l2_format *f)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	int ret; -	ret = vidioc_try_fmt_vid_out(file, priv, f); +	ret = coda_try_fmt_vid_out(file, priv, f);  	if (ret)  		return ret; -	ret = vidioc_s_fmt(ctx, f); +	ret = coda_s_fmt(ctx, f);  	if (ret)  		ctx->colorspace = f->fmt.pix.colorspace;  	return ret;  } -static int vidioc_reqbufs(struct file *file, void *priv, -			  struct v4l2_requestbuffers *reqbufs) +static int coda_reqbufs(struct file *file, void *priv, +			struct v4l2_requestbuffers *reqbufs)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);  } -static int vidioc_querybuf(struct file *file, void *priv, -			   struct v4l2_buffer *buf) +static int coda_querybuf(struct file *file, void *priv, +			 struct v4l2_buffer *buf)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);  } -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static int coda_qbuf(struct file *file, void *priv, +		     struct v4l2_buffer *buf)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);  } -static int vidioc_expbuf(struct file *file, void *priv, -			 struct v4l2_exportbuffer *eb) +static int coda_expbuf(struct file *file, void *priv, +		       struct v4l2_exportbuffer *eb)  {  	struct coda_ctx *ctx = fh_to_ctx(priv); @@ -718,7 +782,8 @@ static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,  		(buf->sequence == (ctx->qsequence - 1)));  } -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static int coda_dqbuf(struct file *file, void *priv, +		      struct v4l2_buffer *buf)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	int ret; @@ -738,24 +803,24 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)  	return ret;  } -static int vidioc_create_bufs(struct file *file, void *priv, -			      struct v4l2_create_buffers *create) +static int coda_create_bufs(struct file *file, void *priv, +			    struct v4l2_create_buffers *create)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	return v4l2_m2m_create_bufs(file, ctx->m2m_ctx, create);  } -static int vidioc_streamon(struct file *file, void *priv, -			   enum v4l2_buf_type type) +static int coda_streamon(struct file *file, void *priv, +			 enum v4l2_buf_type type)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);  } -static int vidioc_streamoff(struct file *file, void *priv, -			    enum v4l2_buf_type type) +static int coda_streamoff(struct file *file, void *priv, +			  enum v4l2_buf_type type)  {  	struct coda_ctx *ctx = fh_to_ctx(priv);  	int ret; @@ -772,23 +837,34 @@ static int vidioc_streamoff(struct file *file, void *priv,  	return ret;  } -static int vidioc_decoder_cmd(struct file *file, void *fh, -			      struct v4l2_decoder_cmd *dc) +static int coda_try_decoder_cmd(struct file *file, void *fh, +				struct v4l2_decoder_cmd *dc)  { -	struct coda_ctx *ctx = fh_to_ctx(fh); -  	if (dc->cmd != V4L2_DEC_CMD_STOP)  		return -EINVAL; -	if ((dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) || -	    (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)) +	if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK)  		return -EINVAL; -	if (dc->stop.pts != 0) +	if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0))  		return -EINVAL; +	return 0; +} + +static int coda_decoder_cmd(struct file *file, void *fh, +			    struct v4l2_decoder_cmd *dc) +{ +	struct coda_ctx *ctx = fh_to_ctx(fh); +	int ret; + +	ret = coda_try_decoder_cmd(file, fh, dc); +	if (ret < 0) +		return ret; + +	/* Ignore decoder stop command silently in encoder context */  	if (ctx->inst_type != CODA_INST_DECODER) -		return -EINVAL; +		return 0;  	/* Set the strem-end flag on this context */  	ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG; @@ -796,8 +872,8 @@ static int vidioc_decoder_cmd(struct file *file, void *fh,  	return 0;  } -static int vidioc_subscribe_event(struct v4l2_fh *fh, -				  const struct v4l2_event_subscription *sub) +static int coda_subscribe_event(struct v4l2_fh *fh, +				const struct v4l2_event_subscription *sub)  {  	switch (sub->type) {  	case V4L2_EVENT_EOS: @@ -808,32 +884,33 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,  }  static const struct v4l2_ioctl_ops coda_ioctl_ops = { -	.vidioc_querycap	= vidioc_querycap, +	.vidioc_querycap	= coda_querycap, -	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, -	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt, -	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap, -	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap, +	.vidioc_enum_fmt_vid_cap = coda_enum_fmt_vid_cap, +	.vidioc_g_fmt_vid_cap	= coda_g_fmt, +	.vidioc_try_fmt_vid_cap	= coda_try_fmt_vid_cap, +	.vidioc_s_fmt_vid_cap	= coda_s_fmt_vid_cap, -	.vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, -	.vidioc_g_fmt_vid_out	= vidioc_g_fmt, -	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out, -	.vidioc_s_fmt_vid_out	= vidioc_s_fmt_vid_out, +	.vidioc_enum_fmt_vid_out = coda_enum_fmt_vid_out, +	.vidioc_g_fmt_vid_out	= coda_g_fmt, +	.vidioc_try_fmt_vid_out	= coda_try_fmt_vid_out, +	.vidioc_s_fmt_vid_out	= coda_s_fmt_vid_out, -	.vidioc_reqbufs		= vidioc_reqbufs, -	.vidioc_querybuf	= vidioc_querybuf, +	.vidioc_reqbufs		= coda_reqbufs, +	.vidioc_querybuf	= coda_querybuf, -	.vidioc_qbuf		= vidioc_qbuf, -	.vidioc_expbuf		= vidioc_expbuf, -	.vidioc_dqbuf		= vidioc_dqbuf, -	.vidioc_create_bufs	= vidioc_create_bufs, +	.vidioc_qbuf		= coda_qbuf, +	.vidioc_expbuf		= coda_expbuf, +	.vidioc_dqbuf		= coda_dqbuf, +	.vidioc_create_bufs	= coda_create_bufs, -	.vidioc_streamon	= vidioc_streamon, -	.vidioc_streamoff	= vidioc_streamoff, +	.vidioc_streamon	= coda_streamon, +	.vidioc_streamoff	= coda_streamoff, -	.vidioc_decoder_cmd	= vidioc_decoder_cmd, +	.vidioc_try_decoder_cmd	= coda_try_decoder_cmd, +	.vidioc_decoder_cmd	= coda_decoder_cmd, -	.vidioc_subscribe_event = vidioc_subscribe_event, +	.vidioc_subscribe_event = coda_subscribe_event,  	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,  }; @@ -1357,7 +1434,7 @@ static void coda_buf_queue(struct vb2_buffer *vb)  	if (q_data->fourcc == V4L2_PIX_FMT_H264 &&  	    vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {  		/* -		 * For backwards compatiblity, queuing an empty buffer marks +		 * For backwards compatibility, queuing an empty buffer marks  		 * the stream end  		 */  		if (vb2_get_plane_payload(vb, 0) == 0) @@ -1928,8 +2005,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)  	if (!(ctx->streamon_out & ctx->streamon_cap))  		return 0; -	/* Allow device_run with no buffers queued and after streamoff */ -	v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true); +	/* Allow decoder device_run with no new buffers queued */ +	if (ctx->inst_type == CODA_INST_DECODER) +		v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true);  	ctx->gopcounter = ctx->params.gop_size - 1;  	buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); @@ -2071,10 +2149,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)  	coda_setup_iram(ctx);  	if (dst_fourcc == V4L2_PIX_FMT_H264) { -		value  = (FMO_SLICE_SAVE_BUF_SIZE << 7); -		value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET; -		value |=  0 & CODA_FMOPARAM_SLICENUM_MASK;  		if (dev->devtype->product == CODA_DX6) { +			value = FMO_SLICE_SAVE_BUF_SIZE << 7;  			coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);  		} else {  			coda_write(dev, ctx->iram_info.search_ram_paddr, @@ -2193,7 +2269,7 @@ out:  	return ret;  } -static int coda_stop_streaming(struct vb2_queue *q) +static void coda_stop_streaming(struct vb2_queue *q)  {  	struct coda_ctx *ctx = vb2_get_drv_priv(q);  	struct coda_dev *dev = ctx->dev; @@ -2219,8 +2295,6 @@ static int coda_stop_streaming(struct vb2_queue *q)  			ctx->bitstream.vaddr, ctx->bitstream.size);  		ctx->runcounter = 0;  	} - -	return 0;  }  static struct vb2_ops coda_qops = { @@ -2352,7 +2426,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	src_vq->ops = &coda_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops; -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -2364,14 +2438,20 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	dst_vq->ops = &coda_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops; -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	return vb2_queue_init(dst_vq);  }  static int coda_next_free_instance(struct coda_dev *dev)  { -	return ffz(dev->instance_mask); +	int idx = ffz(dev->instance_mask); + +	if ((idx < 0) || +	    (dev->devtype->product == CODA_DX6 && idx > CODADX6_MAX_INSTANCES)) +		return -EBUSY; + +	return idx;  }  static int coda_open(struct file *file) @@ -2386,8 +2466,8 @@ static int coda_open(struct file *file)  		return -ENOMEM;  	idx = coda_next_free_instance(dev); -	if (idx >= CODA_MAX_INSTANCES) { -		ret = -EBUSY; +	if (idx < 0) { +		ret = idx;  		goto err_coda_max;  	}  	set_bit(idx, &dev->instance_mask); @@ -2719,7 +2799,6 @@ static void coda_finish_encode(struct coda_ctx *ctx)  	dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);  	/* Get results from the coda */ -	coda_read(dev, CODA_RET_ENC_PIC_TYPE);  	start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);  	wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)); @@ -2739,7 +2818,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)  	coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);  	coda_read(dev, CODA_RET_ENC_PIC_FLAG); -	if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { +	if (coda_read(dev, CODA_RET_ENC_PIC_TYPE) == 0) {  		dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;  		dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;  	} else { @@ -2748,6 +2827,9 @@ static void coda_finish_encode(struct coda_ctx *ctx)  	}  	dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; +	dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst_buf->v4l2_buf.flags |= +		src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  	dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;  	v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); @@ -2861,21 +2943,6 @@ static bool coda_firmware_supported(u32 vernum)  	return false;  } -static char *coda_product_name(int product) -{ -	static char buf[9]; - -	switch (product) { -	case CODA_DX6: -		return "CodaDx6"; -	case CODA_7541: -		return "CODA7541"; -	default: -		snprintf(buf, sizeof(buf), "(0x%04x)", product); -		return buf; -	} -} -  static int coda_hw_init(struct coda_dev *dev)  {  	u16 product, major, minor, release; @@ -3166,7 +3233,7 @@ static int coda_probe(struct platform_device *pdev)  	}  	if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, -		IRQF_ONESHOT, CODA_NAME, dev) < 0) { +		IRQF_ONESHOT, dev_name(&pdev->dev), dev) < 0) {  		dev_err(&pdev->dev, "failed to request irq\n");  		return -ENOENT;  	} @@ -3232,13 +3299,12 @@ static int coda_probe(struct platform_device *pdev)  		dev->iram_size = CODA7_IRAM_SIZE;  		break;  	} -	dev->iram_vaddr = gen_pool_alloc(dev->iram_pool, dev->iram_size); +	dev->iram_vaddr = (unsigned long)gen_pool_dma_alloc(dev->iram_pool, +			dev->iram_size, (dma_addr_t *)&dev->iram_paddr);  	if (!dev->iram_vaddr) {  		dev_err(&pdev->dev, "unable to alloc iram\n");  		return -ENOMEM;  	} -	dev->iram_paddr = gen_pool_virt_to_phys(dev->iram_pool, -						dev->iram_vaddr);  	platform_set_drvdata(pdev, dev); diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 04609cc6eba..bf5eff99452 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -341,14 +341,8 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)  {  	struct vpbe_fh *fh = vb2_get_drv_priv(vq);  	struct vpbe_layer *layer = fh->layer; -	struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;  	int ret; -	/* If buffer queue is empty, return error */ -	if (list_empty(&layer->dma_queue)) { -		v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); -		return -EINVAL; -	}  	/* Get the next frame from the buffer queue */  	layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,  				struct vpbe_disp_buffer, list); @@ -361,8 +355,17 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)  	/* Set parameters in OSD and VENC */  	ret = vpbe_set_osd_display_params(fh->disp_dev, layer); -	if (ret < 0) +	if (ret < 0) { +		struct vpbe_disp_buffer *buf, *tmp; + +		vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_QUEUED); +		list_for_each_entry_safe(buf, tmp, &layer->dma_queue, list) { +			list_del(&buf->list); +			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); +		} +  		return ret; +	}  	/*  	 * if request format is yuv420 semiplanar, need to @@ -374,23 +377,36 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)  	return ret;  } -static int vpbe_stop_streaming(struct vb2_queue *vq) +static void vpbe_stop_streaming(struct vb2_queue *vq)  {  	struct vpbe_fh *fh = vb2_get_drv_priv(vq);  	struct vpbe_layer *layer = fh->layer; +	struct vpbe_display *disp = fh->disp_dev; +	unsigned long flags;  	if (!vb2_is_streaming(vq)) -		return 0; +		return;  	/* release all active buffers */ +	spin_lock_irqsave(&disp->dma_queue_lock, flags); +	if (layer->cur_frm == layer->next_frm) { +		vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_ERROR); +	} else { +		if (layer->cur_frm != NULL) +			vb2_buffer_done(&layer->cur_frm->vb, +					VB2_BUF_STATE_ERROR); +		if (layer->next_frm != NULL) +			vb2_buffer_done(&layer->next_frm->vb, +					VB2_BUF_STATE_ERROR); +	} +  	while (!list_empty(&layer->dma_queue)) {  		layer->next_frm = list_entry(layer->dma_queue.next,  						struct vpbe_disp_buffer, list);  		list_del(&layer->next_frm->list);  		vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR);  	} - -	return 0; +	spin_unlock_irqrestore(&disp->dma_queue_lock, flags);  }  static struct vb2_ops video_qops = { @@ -672,29 +688,6 @@ static int vpbe_try_format(struct vpbe_display *disp_dev,  	return 0;  } -static int vpbe_display_g_priority(struct file *file, void *priv, -				enum v4l2_priority *p) -{ -	struct vpbe_fh *fh = file->private_data; -	struct vpbe_layer *layer = fh->layer; - -	*p = v4l2_prio_max(&layer->prio); - -	return 0; -} - -static int vpbe_display_s_priority(struct file *file, void *priv, -				enum v4l2_priority p) -{ -	struct vpbe_fh *fh = file->private_data; -	struct vpbe_layer *layer = fh->layer; -	int ret; - -	ret = v4l2_prio_change(&layer->prio, &fh->prio, p); - -	return ret; -} -  static int vpbe_display_querycap(struct file *file, void  *priv,  			       struct v4l2_capability *cap)  { @@ -1415,7 +1408,8 @@ static int vpbe_display_reqbufs(struct file *file, void *priv,  	q->ops = &video_qops;  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct vpbe_disp_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->min_buffers_needed = 1;  	ret = vb2_queue_init(q);  	if (ret) { @@ -1483,6 +1477,7 @@ static int vpbe_display_open(struct file *file)  {  	struct vpbe_fh *fh = NULL;  	struct vpbe_layer *layer = video_drvdata(file); +	struct video_device *vdev = video_devdata(file);  	struct vpbe_display *disp_dev = layer->disp_dev;  	struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;  	struct osd_state *osd_device = disp_dev->osd_device; @@ -1495,6 +1490,7 @@ static int vpbe_display_open(struct file *file)  			"unable to allocate memory for file handle object\n");  		return -ENOMEM;  	} +	v4l2_fh_init(&fh->fh, vdev);  	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,  			"vpbe display open plane = %d\n",  			layer->device_id); @@ -1523,9 +1519,7 @@ static int vpbe_display_open(struct file *file)  	layer->usrs++;  	/* Set io_allowed member to false */  	fh->io_allowed = 0; -	/* Initialize priority of this instance to default priority */ -	fh->prio = V4L2_PRIORITY_UNSET; -	v4l2_prio_open(&layer->prio, &fh->prio); +	v4l2_fh_add(&fh->fh);  	v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,  			"vpbe display device opened successfully\n");  	return 0; @@ -1580,8 +1574,9 @@ static int vpbe_display_release(struct file *file)  		osd_device->ops.release_layer(osd_device,  				layer->layer_info.id);  	} -	/* Close the priority */ -	v4l2_prio_close(&layer->prio, fh->prio); + +	v4l2_fh_del(&fh->fh); +	v4l2_fh_exit(&fh->fh);  	file->private_data = NULL;  	mutex_unlock(&layer->opslock); @@ -1609,8 +1604,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {  	.vidioc_cropcap		 = vpbe_display_cropcap,  	.vidioc_g_crop		 = vpbe_display_g_crop,  	.vidioc_s_crop		 = vpbe_display_s_crop, -	.vidioc_g_priority	 = vpbe_display_g_priority, -	.vidioc_s_priority	 = vpbe_display_s_priority,  	.vidioc_s_std		 = vpbe_display_s_std,  	.vidioc_g_std		 = vpbe_display_g_std,  	.vidioc_enum_output	 = vpbe_display_enum_output, @@ -1690,8 +1683,6 @@ static int init_vpbe_layer(int i, struct vpbe_display *disp_dev,  	vpbe_display_layer->layer_info.id =  		((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); -	/* Initialize prio member of layer object */ -	v4l2_prio_init(&vpbe_display_layer->prio);  	return 0;  } @@ -1718,6 +1709,7 @@ static int register_device(struct vpbe_layer *vpbe_display_layer,  	vpbe_display_layer->disp_dev = disp_dev;  	/* set the driver data in platform device */  	platform_set_drvdata(pdev, disp_dev); +	set_bit(V4L2_FL_USE_FH_PRIO, &vpbe_display_layer->video_dev.flags);  	video_set_drvdata(&vpbe_display_layer->video_dev,  			  vpbe_display_layer); @@ -1785,7 +1777,7 @@ static int vpbe_display_probe(struct platform_device *pdev)  	}  	irq = res->start; -	err = devm_request_irq(&pdev->dev, irq, venc_isr, IRQF_DISABLED, +	err = devm_request_irq(&pdev->dev, irq, venc_isr, 0,  			       VPBE_DISPLAY_DRIVER, disp_dev);  	if (err) {  		v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 93609091cb2..a51bda2fb63 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -498,6 +498,7 @@ unlock:  static int vpfe_open(struct file *file)  {  	struct vpfe_device *vpfe_dev = video_drvdata(file); +	struct video_device *vdev = video_devdata(file);  	struct vpfe_fh *fh;  	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); @@ -517,6 +518,7 @@ static int vpfe_open(struct file *file)  	/* store pointer to fh in private_data member of file */  	file->private_data = fh;  	fh->vpfe_dev = vpfe_dev; +	v4l2_fh_init(&fh->fh, vdev);  	mutex_lock(&vpfe_dev->lock);  	/* If decoder is not initialized. initialize it */  	if (!vpfe_dev->initialized) { @@ -529,9 +531,7 @@ static int vpfe_open(struct file *file)  	vpfe_dev->usrs++;  	/* Set io_allowed member to false */  	fh->io_allowed = 0; -	/* Initialize priority of this instance to default priority */ -	fh->prio = V4L2_PRIORITY_UNSET; -	v4l2_prio_open(&vpfe_dev->prio, &fh->prio); +	v4l2_fh_add(&fh->fh);  	mutex_unlock(&vpfe_dev->lock);  	return 0;  } @@ -688,7 +688,7 @@ static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)  	frame_format = ccdc_dev->hw_ops.get_frame_format();  	if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {  		return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, -				    IRQF_DISABLED, "vpfe_capture1", +				    0, "vpfe_capture1",  				    vpfe_dev);  	}  	return 0; @@ -734,12 +734,14 @@ static int vpfe_release(struct file *file)  		}  		vpfe_dev->io_usrs = 0;  		vpfe_dev->numbuffers = config_params.numbuffers; +		videobuf_stop(&vpfe_dev->buffer_queue); +		videobuf_mmap_free(&vpfe_dev->buffer_queue);  	}  	/* Decrement device usrs counter */  	vpfe_dev->usrs--; -	/* Close the priority */ -	v4l2_prio_close(&vpfe_dev->prio, fh->prio); +	v4l2_fh_del(&fh->fh); +	v4l2_fh_exit(&fh->fh);  	/* If this is the last file handle */  	if (!vpfe_dev->usrs) {  		vpfe_dev->initialized = 0; @@ -1215,7 +1217,7 @@ static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)  	}  	ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, -					 core, s_std, std_id); +					 video, s_std, std_id);  	if (ret < 0) {  		v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");  		goto unlock_out; @@ -1863,7 +1865,7 @@ static int vpfe_probe(struct platform_device *pdev)  	}  	vpfe_dev->ccdc_irq1 = res1->start; -	ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, +	ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0,  			  "vpfe_capture0", vpfe_dev);  	if (0 != ret) { @@ -1908,14 +1910,13 @@ static int vpfe_probe(struct platform_device *pdev)  	/* Initialize field of the device objects */  	vpfe_dev->numbuffers = config_params.numbuffers; -	/* Initialize prio member of device object */ -	v4l2_prio_init(&vpfe_dev->prio);  	/* register video device */  	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,  		"trying to register vpfe device.\n");  	v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,  		"video_dev=%x\n", (int)&vpfe_dev->video_dev);  	vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +	set_bit(V4L2_FL_USE_FH_PRIO, &vpfe_dev->video_dev->flags);  	ret = video_register_device(vpfe_dev->video_dev,  				    VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 1089834a4ef..1e4ec697fb1 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1,5 +1,6 @@  /*   * Copyright (C) 2009 Texas Instruments Inc + * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -65,14 +66,26 @@ static struct vpif_config_params config_params = {  	.channel_bufsize[1] = 720 * 576 * 2,  }; +#define VPIF_DRIVER_NAME	"vpif_capture" +  /* global variables */  static struct vpif_device vpif_obj = { {NULL} };  static struct device *vpif_dev;  static void vpif_calculate_offsets(struct channel_obj *ch);  static void vpif_config_addr(struct channel_obj *ch, int muxmode); +static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = { {1, 1} }; + +/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */ +static int ycmux_mode; + +static inline struct vpif_cap_buffer *to_vpif_buffer(struct vb2_buffer *vb) +{ +	return container_of(vb, struct vpif_cap_buffer, vb); +} +  /** - * buffer_prepare :  callback function for buffer prepare + * vpif_buffer_prepare :  callback function for buffer prepare   * @vb: ptr to vb2_buffer   *   * This is the callback function for buffer prepare when vb2_qbuf() @@ -81,10 +94,8 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode);   */  static int vpif_buffer_prepare(struct vb2_buffer *vb)  { -	/* Get the file handle object and channel object */ -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);  	struct vb2_queue *q = vb->vb2_queue; -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(q);  	struct common_obj *common;  	unsigned long addr; @@ -92,26 +103,22 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)  	common = &ch->common[VPIF_VIDEO_INDEX]; -	if (vb->state != VB2_BUF_STATE_ACTIVE && -		vb->state != VB2_BUF_STATE_PREPARED) { -		vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); -		if (vb2_plane_vaddr(vb, 0) && -		vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) -			goto exit; -		addr = vb2_dma_contig_plane_dma_addr(vb, 0); +	vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); +	if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) +		return -EINVAL; -		if (q->streaming) { -			if (!IS_ALIGNED((addr + common->ytop_off), 8) || -				!IS_ALIGNED((addr + common->ybtm_off), 8) || -				!IS_ALIGNED((addr + common->ctop_off), 8) || -				!IS_ALIGNED((addr + common->cbtm_off), 8)) -				goto exit; -		} +	vb->v4l2_buf.field = common->fmt.fmt.pix.field; + +	addr = vb2_dma_contig_plane_dma_addr(vb, 0); +	if (!IS_ALIGNED((addr + common->ytop_off), 8) || +		!IS_ALIGNED((addr + common->ybtm_off), 8) || +		!IS_ALIGNED((addr + common->ctop_off), 8) || +		!IS_ALIGNED((addr + common->cbtm_off), 8)) { +		vpif_dbg(1, debug, "offset is not aligned\n"); +		return -EINVAL;  	} +  	return 0; -exit: -	vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n"); -	return -EINVAL;  }  /** @@ -131,49 +138,26 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,  				unsigned int *nbuffers, unsigned int *nplanes,  				unsigned int sizes[], void *alloc_ctxs[])  { -	/* Get the file handle object and channel object */ -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common; -	unsigned long size;  	common = &ch->common[VPIF_VIDEO_INDEX];  	vpif_dbg(2, debug, "vpif_buffer_setup\n"); -	/* If memory type is not mmap, return */ -	if (V4L2_MEMORY_MMAP == common->memory) { -		/* Calculate the size of the buffer */ -		size = config_params.channel_bufsize[ch->channel_id]; -		/* -		 * Checking if the buffer size exceeds the available buffer -		 * ycmux_mode = 0 means 1 channel mode HD and -		 * ycmux_mode = 1 means 2 channels mode SD -		 */ -		if (ch->vpifparams.std_info.ycmux_mode == 0) { -			if (config_params.video_limit[ch->channel_id]) -				while (size * *nbuffers > -					(config_params.video_limit[0] -						+ config_params.video_limit[1])) -					(*nbuffers)--; -		} else { -			if (config_params.video_limit[ch->channel_id]) -				while (size * *nbuffers > -				config_params.video_limit[ch->channel_id]) -					(*nbuffers)--; -		} - -	} else { -		size = common->fmt.fmt.pix.sizeimage; -	} +	if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage) +		return -EINVAL; -	if (*nbuffers < config_params.min_numbuffers) -		*nbuffers = config_params.min_numbuffers; +	if (vq->num_buffers + *nbuffers < 3) +		*nbuffers = 3 - vq->num_buffers;  	*nplanes = 1; -	sizes[0] = size; +	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;  	alloc_ctxs[0] = common->alloc_ctx; +	/* Calculate the offset for Y and C data in the buffer */ +	vpif_calculate_offsets(ch); +  	return 0;  } @@ -183,11 +167,8 @@ static int vpif_buffer_queue_setup(struct vb2_queue *vq,   */  static void vpif_buffer_queue(struct vb2_buffer *vb)  { -	/* Get the file handle object and channel object */ -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); -	struct channel_obj *ch = fh->channel; -	struct vpif_cap_buffer *buf = container_of(vb, -				struct vpif_cap_buffer, vb); +	struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue); +	struct vpif_cap_buffer *buf = to_vpif_buffer(vb);  	struct common_obj *common;  	unsigned long flags; @@ -202,108 +183,25 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)  }  /** - * vpif_buf_cleanup : Callback function to free buffer + * vpif_start_streaming : Starts the DMA engine for streaming   * @vb: ptr to vb2_buffer - * - * This function is called from the videobuf2 layer to free memory - * allocated to  the buffers + * @count: number of buffers   */ -static void vpif_buf_cleanup(struct vb2_buffer *vb) -{ -	/* Get the file handle object and channel object */ -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); -	struct vpif_cap_buffer *buf = container_of(vb, -					struct vpif_cap_buffer, vb); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; -	unsigned long flags; - -	common = &ch->common[VPIF_VIDEO_INDEX]; - -	spin_lock_irqsave(&common->irqlock, flags); -	if (vb->state == VB2_BUF_STATE_ACTIVE) -		list_del_init(&buf->list); -	spin_unlock_irqrestore(&common->irqlock, flags); - -} - -static void vpif_wait_prepare(struct vb2_queue *vq) -{ -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; - -	common = &ch->common[VPIF_VIDEO_INDEX]; -	mutex_unlock(&common->lock); -} - -static void vpif_wait_finish(struct vb2_queue *vq) -{ -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; - -	common = &ch->common[VPIF_VIDEO_INDEX]; -	mutex_lock(&common->lock); -} - -static int vpif_buffer_init(struct vb2_buffer *vb) -{ -	struct vpif_cap_buffer *buf = container_of(vb, -					struct vpif_cap_buffer, vb); - -	INIT_LIST_HEAD(&buf->list); - -	return 0; -} - -static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = -	{ {1, 1} }; -  static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  {  	struct vpif_capture_config *vpif_config_data =  					vpif_dev->platform_data; -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct vpif_params *vpif = &ch->vpifparams; -	unsigned long addr = 0; -	unsigned long flags; +	struct vpif_cap_buffer *buf, *tmp; +	unsigned long addr, flags;  	int ret; -	/* If buffer queue is empty, return error */  	spin_lock_irqsave(&common->irqlock, flags); -	if (list_empty(&common->dma_queue)) { -		spin_unlock_irqrestore(&common->irqlock, flags); -		vpif_dbg(1, debug, "buffer queue is empty\n"); -		return -EIO; -	} -	/* Get the next frame from the buffer queue */ -	common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, -				    struct vpif_cap_buffer, list); -	/* Remove buffer from the buffer queue */ -	list_del(&common->cur_frm->list); -	spin_unlock_irqrestore(&common->irqlock, flags); -	/* Mark state of the current frame to active */ -	common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; -	/* Initialize field_id and started member */ +	/* Initialize field_id */  	ch->field_id = 0; -	common->started = 1; -	addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); - -	/* Calculate the offset for Y and C data in the buffer */ -	vpif_calculate_offsets(ch); - -	if ((vpif->std_info.frm_fmt && -	    ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) && -	     (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) || -	    (!vpif->std_info.frm_fmt && -	     (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { -		vpif_dbg(1, debug, "conflict in field format and std format\n"); -		return -EINVAL; -	}  	/* configure 1 or 2 channel mode */  	if (vpif_config_data->setup_input_channel_mode) { @@ -311,21 +209,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  			setup_input_channel_mode(vpif->std_info.ycmux_mode);  		if (ret < 0) {  			vpif_dbg(1, debug, "can't set vpif channel mode\n"); -			return ret; +			goto err;  		}  	} +	ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); +	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { +		vpif_dbg(1, debug, "stream on failed in subdev\n"); +		goto err; +	} +  	/* Call vpif_set_params function to set the parameters and addresses */  	ret = vpif_set_video_params(vpif, ch->channel_id); -  	if (ret < 0) {  		vpif_dbg(1, debug, "can't set video params\n"); -		return ret; +		goto err;  	} -	common->started = ret; +	ycmux_mode = ret;  	vpif_config_addr(ch, ret); +	/* Get the next frame from the buffer queue */ +	common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, +				    struct vpif_cap_buffer, list); +	/* Remove buffer from the buffer queue */ +	list_del(&common->cur_frm->list); +	spin_unlock_irqrestore(&common->irqlock, flags); +	/* Mark state of the current frame to active */ +	common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + +	addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); +  	common->set_addr(addr + common->ytop_off,  			 addr + common->ybtm_off,  			 addr + common->ctop_off, @@ -336,36 +250,76 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  	 * VPIF register  	 */  	channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; -	if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) { +	if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {  		channel0_intr_assert();  		channel0_intr_enable(1);  		enable_channel0(1);  	} -	if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || -	    (common->started == 2)) { +	if (VPIF_CHANNEL1_VIDEO == ch->channel_id || +		ycmux_mode == 2) {  		channel1_intr_assert();  		channel1_intr_enable(1);  		enable_channel1(1);  	}  	return 0; + +err: +	list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { +		list_del(&buf->list); +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); +	} +	spin_unlock_irqrestore(&common->irqlock, flags); + +	return ret;  } -/* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +/** + * vpif_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpif_stop_streaming(struct vb2_queue *vq)  { -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common;  	unsigned long flags; - -	if (!vb2_is_streaming(vq)) -		return 0; +	int ret;  	common = &ch->common[VPIF_VIDEO_INDEX]; +	/* Disable channel as per its device type and channel id */ +	if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { +		enable_channel0(0); +		channel0_intr_enable(0); +	} +	if (VPIF_CHANNEL1_VIDEO == ch->channel_id || +		ycmux_mode == 2) { +		enable_channel1(0); +		channel1_intr_enable(0); +	} + +	ycmux_mode = 0; + +	ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); +	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) +		vpif_dbg(1, debug, "stream off failed in subdev\n"); +  	/* release all active buffers */  	spin_lock_irqsave(&common->irqlock, flags); +	if (common->cur_frm == common->next_frm) { +		vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); +	} else { +		if (common->cur_frm != NULL) +			vb2_buffer_done(&common->cur_frm->vb, +					VB2_BUF_STATE_ERROR); +		if (common->next_frm != NULL) +			vb2_buffer_done(&common->next_frm->vb, +					VB2_BUF_STATE_ERROR); +	} +  	while (!list_empty(&common->dma_queue)) {  		common->next_frm = list_entry(common->dma_queue.next,  						struct vpif_cap_buffer, list); @@ -373,19 +327,13 @@ static int vpif_stop_streaming(struct vb2_queue *vq)  		vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);  	}  	spin_unlock_irqrestore(&common->irqlock, flags); - -	return 0;  }  static struct vb2_ops video_qops = {  	.queue_setup		= vpif_buffer_queue_setup, -	.wait_prepare		= vpif_wait_prepare, -	.wait_finish		= vpif_wait_finish, -	.buf_init		= vpif_buffer_init,  	.buf_prepare		= vpif_buffer_prepare,  	.start_streaming	= vpif_start_streaming,  	.stop_streaming		= vpif_stop_streaming, -	.buf_cleanup		= vpif_buf_cleanup,  	.buf_queue		= vpif_buffer_queue,  }; @@ -462,9 +410,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)  	for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {  		common = &ch->common[i];  		/* skip If streaming is not started in this channel */ -		if (0 == common->started) -			continue; -  		/* Check the field format */  		if (1 == ch->vpifparams.std_info.frm_fmt) {  			/* Progressive mode */ @@ -666,11 +611,6 @@ static void vpif_config_format(struct channel_obj *ch)  	vpif_dbg(2, debug, "vpif_config_format\n");  	common->fmt.fmt.pix.field = V4L2_FIELD_ANY; -	if (config_params.numbuffers[ch->channel_id] == 0) -		common->memory = V4L2_MEMORY_USERPTR; -	else -		common->memory = V4L2_MEMORY_MMAP; -  	common->fmt.fmt.pix.sizeimage  	    = config_params.channel_bufsize[ch->channel_id]; @@ -820,425 +760,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)  }  /** - * vpif_mmap : It is used to map kernel space buffers into user spaces - * @filep: file pointer - * @vma: ptr to vm_area_struct - */ -static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) -{ -	/* Get the channel object and file handle object */ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); -	int ret; - -	vpif_dbg(2, debug, "vpif_mmap\n"); - -	if (mutex_lock_interruptible(&common->lock)) -		return -ERESTARTSYS; -	ret = vb2_mmap(&common->buffer_queue, vma); -	mutex_unlock(&common->lock); -	return ret; -} - -/** - * vpif_poll: It is used for select/poll system call - * @filep: file pointer - * @wait: poll table to wait - */ -static unsigned int vpif_poll(struct file *filep, poll_table * wait) -{ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *channel = fh->channel; -	struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]); -	unsigned int res = 0; - -	vpif_dbg(2, debug, "vpif_poll\n"); - -	if (common->started) { -		mutex_lock(&common->lock); -		res = vb2_poll(&common->buffer_queue, filep, wait); -		mutex_unlock(&common->lock); -	} -	return res; -} - -/** - * vpif_open : vpif open handler - * @filep: file ptr - * - * It creates object of file handle structure and stores it in private_data - * member of filepointer - */ -static int vpif_open(struct file *filep) -{ -	struct video_device *vdev = video_devdata(filep); -	struct common_obj *common; -	struct video_obj *vid_ch; -	struct channel_obj *ch; -	struct vpif_fh *fh; - -	vpif_dbg(2, debug, "vpif_open\n"); - -	ch = video_get_drvdata(vdev); - -	vid_ch = &ch->video; -	common = &ch->common[VPIF_VIDEO_INDEX]; - -	/* Allocate memory for the file handle object */ -	fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); -	if (NULL == fh) { -		vpif_err("unable to allocate memory for file handle object\n"); -		return -ENOMEM; -	} - -	if (mutex_lock_interruptible(&common->lock)) { -		kfree(fh); -		return -ERESTARTSYS; -	} -	/* store pointer to fh in private_data member of filep */ -	filep->private_data = fh; -	fh->channel = ch; -	fh->initialized = 0; -	/* If decoder is not initialized. initialize it */ -	if (!ch->initialized) { -		fh->initialized = 1; -		ch->initialized = 1; -		memset(&(ch->vpifparams), 0, sizeof(struct vpif_params)); -	} -	/* Increment channel usrs counter */ -	ch->usrs++; -	/* Set io_allowed member to false */ -	fh->io_allowed[VPIF_VIDEO_INDEX] = 0; -	/* Initialize priority of this instance to default priority */ -	fh->prio = V4L2_PRIORITY_UNSET; -	v4l2_prio_open(&ch->prio, &fh->prio); -	mutex_unlock(&common->lock); -	return 0; -} - -/** - * vpif_release : function to clean up file close - * @filep: file pointer - * - * This function deletes buffer queue, frees the buffers and the vpif file - * handle - */ -static int vpif_release(struct file *filep) -{ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; - -	vpif_dbg(2, debug, "vpif_release\n"); - -	common = &ch->common[VPIF_VIDEO_INDEX]; - -	mutex_lock(&common->lock); -	/* if this instance is doing IO */ -	if (fh->io_allowed[VPIF_VIDEO_INDEX]) { -		/* Reset io_usrs member of channel object */ -		common->io_usrs = 0; -		/* Disable channel as per its device type and channel id */ -		if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { -			enable_channel0(0); -			channel0_intr_enable(0); -		} -		if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || -		    (2 == common->started)) { -			enable_channel1(0); -			channel1_intr_enable(0); -		} -		common->started = 0; -		/* Free buffers allocated */ -		vb2_queue_release(&common->buffer_queue); -		vb2_dma_contig_cleanup_ctx(common->alloc_ctx); -	} - -	/* Decrement channel usrs counter */ -	ch->usrs--; - -	/* Close the priority */ -	v4l2_prio_close(&ch->prio, fh->prio); - -	if (fh->initialized) -		ch->initialized = 0; - -	mutex_unlock(&common->lock); -	filep->private_data = NULL; -	kfree(fh); -	return 0; -} - -/** - * vpif_reqbufs() - request buffer handler - * @file: file ptr - * @priv: file handle - * @reqbuf: request buffer structure ptr - */ -static int vpif_reqbufs(struct file *file, void *priv, -			struct v4l2_requestbuffers *reqbuf) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; -	u8 index = 0; -	struct vb2_queue *q; -	int ret; - -	vpif_dbg(2, debug, "vpif_reqbufs\n"); - -	/** -	 * This file handle has not initialized the channel, -	 * It is not allowed to do settings -	 */ -	if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) -	    || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_dbg(1, debug, "Channel Busy\n"); -			return -EBUSY; -		} -	} - -	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev) -		return -EINVAL; - -	index = VPIF_VIDEO_INDEX; - -	common = &ch->common[index]; - -	if (0 != common->io_usrs) -		return -EBUSY; - -	/* Initialize videobuf2 queue as per the buffer type */ -	common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); -	if (IS_ERR(common->alloc_ctx)) { -		vpif_err("Failed to get the context\n"); -		return PTR_ERR(common->alloc_ctx); -	} -	q = &common->buffer_queue; -	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -	q->io_modes = VB2_MMAP | VB2_USERPTR; -	q->drv_priv = fh; -	q->ops = &video_qops; -	q->mem_ops = &vb2_dma_contig_memops; -	q->buf_struct_size = sizeof(struct vpif_cap_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - -	ret = vb2_queue_init(q); -	if (ret) { -		vpif_err("vpif_capture: vb2_queue_init() failed\n"); -		vb2_dma_contig_cleanup_ctx(common->alloc_ctx); -		return ret; -	} -	/* Set io allowed member of file handle to TRUE */ -	fh->io_allowed[index] = 1; -	/* Increment io usrs member of channel object to 1 */ -	common->io_usrs = 1; -	/* Store type of memory requested in channel object */ -	common->memory = reqbuf->memory; -	INIT_LIST_HEAD(&common->dma_queue); - -	/* Allocate buffers */ -	return vb2_reqbufs(&common->buffer_queue, reqbuf); -} - -/** - * vpif_querybuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_querybuf(struct file *file, void *priv, -				struct v4l2_buffer *buf) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	vpif_dbg(2, debug, "vpif_querybuf\n"); - -	if (common->fmt.type != buf->type) -		return -EINVAL; - -	if (common->memory != V4L2_MEMORY_MMAP) { -		vpif_dbg(1, debug, "Invalid memory\n"); -		return -EINVAL; -	} - -	return vb2_querybuf(&common->buffer_queue, buf); -} - -/** - * vpif_qbuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	struct v4l2_buffer tbuf = *buf; - -	vpif_dbg(2, debug, "vpif_qbuf\n"); - -	if (common->fmt.type != tbuf.type) { -		vpif_err("invalid buffer type\n"); -		return -EINVAL; -	} - -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_err("fh io not allowed\n"); -		return -EACCES; -	} - -	return vb2_qbuf(&common->buffer_queue, buf); -} - -/** - * vpif_dqbuf() - query buffer handler - * @file: file ptr - * @priv: file handle - * @buf: v4l2 buffer structure ptr - */ -static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	vpif_dbg(2, debug, "vpif_dqbuf\n"); - -	return vb2_dqbuf(&common->buffer_queue, buf, -			 (file->f_flags & O_NONBLOCK)); -} - -/** - * vpif_streamon() - streamon handler - * @file: file ptr - * @priv: file handle - * @buftype: v4l2 buffer type - */ -static int vpif_streamon(struct file *file, void *priv, -				enum v4l2_buf_type buftype) -{ - -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; -	struct vpif_params *vpif; -	int ret = 0; - -	vpif_dbg(2, debug, "vpif_streamon\n"); - -	vpif = &ch->vpifparams; - -	if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { -		vpif_dbg(1, debug, "buffer type not supported\n"); -		return -EINVAL; -	} - -	/* If file handle is not allowed IO, return error */ -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_dbg(1, debug, "io not allowed\n"); -		return -EACCES; -	} - -	/* If Streaming is already started, return error */ -	if (common->started) { -		vpif_dbg(1, debug, "channel->started\n"); -		return -EBUSY; -	} - -	if ((ch->channel_id == VPIF_CHANNEL0_VIDEO && -	    oth_ch->common[VPIF_VIDEO_INDEX].started && -	    vpif->std_info.ycmux_mode == 0) || -	   ((ch->channel_id == VPIF_CHANNEL1_VIDEO) && -	    (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { -		vpif_dbg(1, debug, "other channel is being used\n"); -		return -EBUSY; -	} - -	ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0); -	if (ret) -		return ret; - -	/* Enable streamon on the sub device */ -	ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); - -	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { -		vpif_dbg(1, debug, "stream on failed in subdev\n"); -		return ret; -	} - -	/* Call vb2_streamon to start streaming in videobuf2 */ -	ret = vb2_streamon(&common->buffer_queue, buftype); -	if (ret) { -		vpif_dbg(1, debug, "vb2_streamon\n"); -		return ret; -	} - -	return ret; -} - -/** - * vpif_streamoff() - streamoff handler - * @file: file ptr - * @priv: file handle - * @buftype: v4l2 buffer type - */ -static int vpif_streamoff(struct file *file, void *priv, -				enum v4l2_buf_type buftype) -{ - -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	int ret; - -	vpif_dbg(2, debug, "vpif_streamoff\n"); - -	if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { -		vpif_dbg(1, debug, "buffer type not supported\n"); -		return -EINVAL; -	} - -	/* If io is allowed for this file handle, return error */ -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_dbg(1, debug, "io not allowed\n"); -		return -EACCES; -	} - -	/* If streaming is not started, return error */ -	if (!common->started) { -		vpif_dbg(1, debug, "channel->started\n"); -		return -EINVAL; -	} - -	/* disable channel */ -	if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { -		enable_channel0(0); -		channel0_intr_enable(0); -	} else { -		enable_channel1(0); -		channel1_intr_enable(0); -	} - -	common->started = 0; - -	ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); - -	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) -		vpif_dbg(1, debug, "stream off failed in subdev\n"); - -	return vb2_streamoff(&common->buffer_queue, buftype); -} - -/**   * vpif_input_to_subdev() - Maps input to sub device   * @vpif_cfg - global config ptr   * @chan_cfg - channel config ptr @@ -1341,8 +862,8 @@ static int vpif_set_input(   */  static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	int ret = 0;  	vpif_dbg(2, debug, "vpif_querystd\n"); @@ -1368,11 +889,22 @@ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)   */  static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input;  	vpif_dbg(2, debug, "vpif_g_std\n"); +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_STD) +		return -ENODATA; +  	*std = ch->video.stdid;  	return 0;  } @@ -1385,31 +917,26 @@ static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)   */  static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	int ret = 0; +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input; +	int ret;  	vpif_dbg(2, debug, "vpif_s_std\n"); -	if (common->started) { -		vpif_err("streaming in progress\n"); -		return -EBUSY; -	} - -	if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || -	    (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_dbg(1, debug, "Channel Busy\n"); -			return -EBUSY; -		} -	} +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; -	ret = v4l2_prio_check(&ch->prio, fh->prio); -	if (0 != ret) -		return ret; +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_STD) +		return -ENODATA; -	fh->initialized = 1; +	if (vb2_is_busy(&common->buffer_queue)) +		return -EBUSY;  	/* Call encoder subdevice function to set the standard */  	ch->video.stdid = std_id; @@ -1425,7 +952,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  	vpif_config_format(ch);  	/* set standard in the sub device */ -	ret = v4l2_subdev_call(ch->sd, core, s_std, std_id); +	ret = v4l2_subdev_call(ch->sd, video, s_std, std_id);  	if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) {  		vpif_dbg(1, debug, "Failed to set standard for sub devices\n");  		return ret; @@ -1444,9 +971,9 @@ static int vpif_enum_input(struct file *file, void *priv,  {  	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct vpif_capture_chan_config *chan_cfg; -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel;  	chan_cfg = &config->chan_config[ch->channel_id]; @@ -1468,8 +995,8 @@ static int vpif_enum_input(struct file *file, void *priv,   */  static int vpif_g_input(struct file *file, void *priv, unsigned int *index)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	*index = ch->input_idx;  	return 0; @@ -1484,35 +1011,19 @@ static int vpif_g_input(struct file *file, void *priv, unsigned int *index)  static int vpif_s_input(struct file *file, void *priv, unsigned int index)  {  	struct vpif_capture_config *config = vpif_dev->platform_data; -	struct vpif_capture_chan_config *chan_cfg; -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	int ret; +	struct vpif_capture_chan_config *chan_cfg;  	chan_cfg = &config->chan_config[ch->channel_id];  	if (index >= chan_cfg->input_count)  		return -EINVAL; -	if (common->started) { -		vpif_err("Streaming in progress\n"); +	if (vb2_is_busy(&common->buffer_queue))  		return -EBUSY; -	} - -	if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || -	    (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_dbg(1, debug, "Channel Busy\n"); -			return -EBUSY; -		} -	} - -	ret = v4l2_prio_check(&ch->prio, fh->prio); -	if (0 != ret) -		return ret; -	fh->initialized = 1;  	return vpif_set_input(config, ch, index);  } @@ -1525,8 +1036,8 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index)  static int vpif_enum_fmt_vid_cap(struct file *file, void  *priv,  					struct v4l2_fmtdesc *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	if (fmt->index != 0) {  		vpif_dbg(1, debug, "Invalid format index\n"); @@ -1555,8 +1066,8 @@ static int vpif_enum_fmt_vid_cap(struct file *file, void  *priv,  static int vpif_try_fmt_vid_cap(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;  	return vpif_check_format(ch, pixfmt, 1); @@ -1572,8 +1083,8 @@ static int vpif_try_fmt_vid_cap(struct file *file, void *priv,  static int vpif_g_fmt_vid_cap(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	/* Check the validity of the buffer type */ @@ -1594,33 +1105,16 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv,  static int vpif_s_fmt_vid_cap(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct v4l2_pix_format *pixfmt;  	int ret = 0;  	vpif_dbg(2, debug, "%s\n", __func__); -	/* If streaming is started, return error */ -	if (common->started) { -		vpif_dbg(1, debug, "Streaming is started\n"); +	if (vb2_is_busy(&common->buffer_queue))  		return -EBUSY; -	} - -	if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || -	    (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_dbg(1, debug, "Channel Busy\n"); -			return -EBUSY; -		} -	} - -	ret = v4l2_prio_check(&ch->prio, fh->prio); -	if (0 != ret) -		return ret; - -	fh->initialized = 1;  	pixfmt = &fmt->fmt.pix;  	/* Check for valid field format */ @@ -1646,7 +1140,7 @@ static int vpif_querycap(struct file *file, void  *priv,  	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;  	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -	snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); +	strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));  	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",  		 dev_name(vpif_dev));  	strlcpy(cap->card, config->card_name, sizeof(cap->card)); @@ -1655,61 +1149,6 @@ static int vpif_querycap(struct file *file, void  *priv,  }  /** - * vpif_g_priority() - get priority handler - * @file: file ptr - * @priv: file handle - * @prio: ptr to v4l2_priority structure - */ -static int vpif_g_priority(struct file *file, void *priv, -			   enum v4l2_priority *prio) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; - -	*prio = v4l2_prio_max(&ch->prio); - -	return 0; -} - -/** - * vpif_s_priority() - set priority handler - * @file: file ptr - * @priv: file handle - * @prio: ptr to v4l2_priority structure - */ -static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; - -	return v4l2_prio_change(&ch->prio, &fh->prio, p); -} - -/** - * vpif_cropcap() - cropcap handler - * @file: file ptr - * @priv: file handle - * @crop: ptr to v4l2_cropcap structure - */ -static int vpif_cropcap(struct file *file, void *priv, -			struct v4l2_cropcap *crop) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type) -		return -EINVAL; - -	crop->bounds.left = 0; -	crop->bounds.top = 0; -	crop->bounds.height = common->height; -	crop->bounds.width = common->width; -	crop->defrect = crop->bounds; -	return 0; -} - -/**   * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler   * @file: file ptr   * @priv: file handle @@ -1719,13 +1158,27 @@ static int  vpif_enum_dv_timings(struct file *file, void *priv,  		     struct v4l2_enum_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input;  	int ret; -	ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) +		return -ENODATA; + +	timings->pad = 0; + +	ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);  	if (ret == -ENOIOCTLCMD || ret == -ENODEV)  		return -EINVAL; +  	return ret;  } @@ -1739,13 +1192,25 @@ static int  vpif_query_dv_timings(struct file *file, void *priv,  		      struct v4l2_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input;  	int ret; +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) +		return -ENODATA; +  	ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);  	if (ret == -ENOIOCTLCMD || ret == -ENODEV)  		return -ENODATA; +  	return ret;  } @@ -1758,19 +1223,34 @@ vpif_query_dv_timings(struct file *file, void *priv,  static int vpif_s_dv_timings(struct file *file, void *priv,  		struct v4l2_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct vpif_params *vpifparams = &ch->vpifparams;  	struct vpif_channel_config_params *std_info = &vpifparams->std_info; +	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct video_obj *vid_ch = &ch->video;  	struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input;  	int ret; +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) +		return -ENODATA; +  	if (timings->type != V4L2_DV_BT_656_1120) {  		vpif_dbg(2, debug, "Timing type not defined\n");  		return -EINVAL;  	} +	if (vb2_is_busy(&common->buffer_queue)) +		return -EBUSY; +  	/* Configure subdevice timings, if any */  	ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);  	if (ret == -ENOIOCTLCMD || ret == -ENODEV) @@ -1846,9 +1326,20 @@ static int vpif_s_dv_timings(struct file *file, void *priv,  static int vpif_g_dv_timings(struct file *file, void *priv,  		struct v4l2_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_capture_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct video_obj *vid_ch = &ch->video; +	struct vpif_capture_chan_config *chan_cfg; +	struct v4l2_input input; + +	if (config->chan_config[ch->channel_id].inputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	input = chan_cfg->inputs[ch->input_idx].input; +	if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS) +		return -ENODATA;  	*timings = vid_ch->dv_timings; @@ -1872,49 +1363,45 @@ static int vpif_log_status(struct file *filep, void *priv)  /* vpif capture ioctl operations */  static const struct v4l2_ioctl_ops vpif_ioctl_ops = { -	.vidioc_querycap        	= vpif_querycap, -	.vidioc_g_priority		= vpif_g_priority, -	.vidioc_s_priority		= vpif_s_priority, +	.vidioc_querycap		= vpif_querycap,  	.vidioc_enum_fmt_vid_cap	= vpif_enum_fmt_vid_cap, -	.vidioc_g_fmt_vid_cap  		= vpif_g_fmt_vid_cap, +	.vidioc_g_fmt_vid_cap		= vpif_g_fmt_vid_cap,  	.vidioc_s_fmt_vid_cap		= vpif_s_fmt_vid_cap,  	.vidioc_try_fmt_vid_cap		= vpif_try_fmt_vid_cap, +  	.vidioc_enum_input		= vpif_enum_input,  	.vidioc_s_input			= vpif_s_input,  	.vidioc_g_input			= vpif_g_input, -	.vidioc_reqbufs         	= vpif_reqbufs, -	.vidioc_querybuf        	= vpif_querybuf, + +	.vidioc_reqbufs			= vb2_ioctl_reqbufs, +	.vidioc_create_bufs		= vb2_ioctl_create_bufs, +	.vidioc_querybuf		= vb2_ioctl_querybuf, +	.vidioc_qbuf			= vb2_ioctl_qbuf, +	.vidioc_dqbuf			= vb2_ioctl_dqbuf, +	.vidioc_expbuf			= vb2_ioctl_expbuf, +	.vidioc_streamon		= vb2_ioctl_streamon, +	.vidioc_streamoff		= vb2_ioctl_streamoff, +  	.vidioc_querystd		= vpif_querystd, -	.vidioc_s_std           	= vpif_s_std, +	.vidioc_s_std			= vpif_s_std,  	.vidioc_g_std			= vpif_g_std, -	.vidioc_qbuf            	= vpif_qbuf, -	.vidioc_dqbuf           	= vpif_dqbuf, -	.vidioc_streamon        	= vpif_streamon, -	.vidioc_streamoff       	= vpif_streamoff, -	.vidioc_cropcap         	= vpif_cropcap, -	.vidioc_enum_dv_timings         = vpif_enum_dv_timings, -	.vidioc_query_dv_timings        = vpif_query_dv_timings, -	.vidioc_s_dv_timings            = vpif_s_dv_timings, -	.vidioc_g_dv_timings            = vpif_g_dv_timings, + +	.vidioc_enum_dv_timings		= vpif_enum_dv_timings, +	.vidioc_query_dv_timings	= vpif_query_dv_timings, +	.vidioc_s_dv_timings		= vpif_s_dv_timings, +	.vidioc_g_dv_timings		= vpif_g_dv_timings, +  	.vidioc_log_status		= vpif_log_status,  };  /* vpif file operations */  static struct v4l2_file_operations vpif_fops = {  	.owner = THIS_MODULE, -	.open = vpif_open, -	.release = vpif_release, +	.open = v4l2_fh_open, +	.release = vb2_fop_release,  	.unlocked_ioctl = video_ioctl2, -	.mmap = vpif_mmap, -	.poll = vpif_poll -}; - -/* vpif video template */ -static struct video_device vpif_video_template = { -	.name		= "vpif", -	.fops		= &vpif_fops, -	.minor		= -1, -	.ioctl_ops	= &vpif_ioctl_ops, +	.mmap = vb2_fop_mmap, +	.poll = vb2_fop_poll  };  /** @@ -1992,7 +1479,9 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,  static int vpif_probe_complete(void)  {  	struct common_obj *common; +	struct video_device *vdev;  	struct channel_obj *ch; +	struct vb2_queue *q;  	int i, j, err, k;  	for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { @@ -2001,17 +1490,52 @@ static int vpif_probe_complete(void)  		common = &(ch->common[VPIF_VIDEO_INDEX]);  		spin_lock_init(&common->irqlock);  		mutex_init(&common->lock); -		ch->video_dev->lock = &common->lock; -		/* Initialize prio member of channel object */ -		v4l2_prio_init(&ch->prio); -		video_set_drvdata(ch->video_dev, ch);  		/* select input 0 */  		err = vpif_set_input(vpif_obj.config, ch, 0);  		if (err)  			goto probe_out; -		err = video_register_device(ch->video_dev, +		/* Initialize vb2 queue */ +		q = &common->buffer_queue; +		q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; +		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; +		q->drv_priv = ch; +		q->ops = &video_qops; +		q->mem_ops = &vb2_dma_contig_memops; +		q->buf_struct_size = sizeof(struct vpif_cap_buffer); +		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +		q->min_buffers_needed = 1; +		q->lock = &common->lock; + +		err = vb2_queue_init(q); +		if (err) { +			vpif_err("vpif_capture: vb2_queue_init() failed\n"); +			goto probe_out; +		} + +		common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); +		if (IS_ERR(common->alloc_ctx)) { +			vpif_err("Failed to get the context\n"); +			err = PTR_ERR(common->alloc_ctx); +			goto probe_out; +		} + +		INIT_LIST_HEAD(&common->dma_queue); + +		/* Initialize the video_device structure */ +		vdev = ch->video_dev; +		strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); +		vdev->release = video_device_release; +		vdev->fops = &vpif_fops; +		vdev->ioctl_ops = &vpif_ioctl_ops; +		vdev->v4l2_dev = &vpif_obj.v4l2_dev; +		vdev->vfl_dir = VFL_DIR_RX; +		vdev->queue = q; +		vdev->lock = &common->lock; +		set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); +		video_set_drvdata(ch->video_dev, ch); +		err = video_register_device(vdev,  					    VFL_TYPE_GRABBER, (j ? 1 : 0));  		if (err)  			goto probe_out; @@ -2024,6 +1548,8 @@ probe_out:  	for (k = 0; k < j; k++) {  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[k]; +		common = &ch->common[k]; +		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);  		/* Unregister video device */  		video_unregister_device(ch->video_dev);  	} @@ -2060,7 +1586,6 @@ static __init int vpif_probe(struct platform_device *pdev)  	struct video_device *vfd;  	struct resource *res;  	int subdev_count; -	size_t size;  	vpif_dev = &pdev->dev; @@ -2078,7 +1603,7 @@ static __init int vpif_probe(struct platform_device *pdev)  	while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {  		err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, -					IRQF_SHARED, "VPIF_Capture", +					IRQF_SHARED, VPIF_DRIVER_NAME,  					(void *)(&vpif_obj.dev[res_idx]->  					channel_id));  		if (err) { @@ -2102,34 +1627,10 @@ static __init int vpif_probe(struct platform_device *pdev)  			goto vpif_unregister;  		} -		/* Initialize field of video device */ -		*vfd = vpif_video_template; -		vfd->v4l2_dev = &vpif_obj.v4l2_dev; -		vfd->release = video_device_release; -		snprintf(vfd->name, sizeof(vfd->name), -			 "VPIF_Capture_DRIVER_V%s", -			 VPIF_CAPTURE_VERSION);  		/* Set video_dev to the video device */  		ch->video_dev = vfd;  	} -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res) { -		size = resource_size(res); -		/* The resources are divided into two equal memory and when we -		 * have HD output we can add them together -		 */ -		for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { -			ch = vpif_obj.dev[j]; -			ch->channel_id = j; -			/* only enabled if second resource exists */ -			config_params.video_limit[ch->channel_id] = 0; -			if (size) -				config_params.video_limit[ch->channel_id] = -									size/2; -		} -	} -  	vpif_obj.config = pdev->dev.platform_data;  	subdev_count = vpif_obj.config->subdev_count; @@ -2154,7 +1655,7 @@ static __init int vpif_probe(struct platform_device *pdev)  			if (!vpif_obj.sd[i]) {  				vpif_err("Error registering v4l2 subdevice\n"); -				err = -ENOMEM; +				err = -ENODEV;  				goto probe_subdev_out;  			}  			v4l2_info(&vpif_obj.v4l2_dev, @@ -2202,6 +1703,7 @@ vpif_unregister:   */  static int vpif_remove(struct platform_device *device)  { +	struct common_obj *common;  	struct channel_obj *ch;  	int i; @@ -2212,6 +1714,8 @@ static int vpif_remove(struct platform_device *device)  	for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i]; +		common = &ch->common[i]; +		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);  		/* Unregister video device */  		video_unregister_device(ch->video_dev);  		kfree(vpif_obj.dev[i]); @@ -2219,7 +1723,7 @@ static int vpif_remove(struct platform_device *device)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  /**   * vpif_suspend: vpif device suspend   */ @@ -2234,18 +1738,20 @@ static int vpif_suspend(struct device *dev)  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i];  		common = &ch->common[VPIF_VIDEO_INDEX]; + +		if (!vb2_is_streaming(&common->buffer_queue)) +			continue; +  		mutex_lock(&common->lock); -		if (ch->usrs && common->io_usrs) { -			/* Disable channel */ -			if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { -				enable_channel0(0); -				channel0_intr_enable(0); -			} -			if (ch->channel_id == VPIF_CHANNEL1_VIDEO || -			    common->started == 2) { -				enable_channel1(0); -				channel1_intr_enable(0); -			} +		/* Disable channel */ +		if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { +			enable_channel0(0); +			channel0_intr_enable(0); +		} +		if (ch->channel_id == VPIF_CHANNEL1_VIDEO || +			ycmux_mode == 2) { +			enable_channel1(0); +			channel1_intr_enable(0);  		}  		mutex_unlock(&common->lock);  	} @@ -2266,40 +1772,35 @@ static int vpif_resume(struct device *dev)  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i];  		common = &ch->common[VPIF_VIDEO_INDEX]; + +		if (!vb2_is_streaming(&common->buffer_queue)) +			continue; +  		mutex_lock(&common->lock); -		if (ch->usrs && common->io_usrs) { -			/* Disable channel */ -			if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { -				enable_channel0(1); -				channel0_intr_enable(1); -			} -			if (ch->channel_id == VPIF_CHANNEL1_VIDEO || -			    common->started == 2) { -				enable_channel1(1); -				channel1_intr_enable(1); -			} +		/* Enable channel */ +		if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { +			enable_channel0(1); +			channel0_intr_enable(1); +		} +		if (ch->channel_id == VPIF_CHANNEL1_VIDEO || +			ycmux_mode == 2) { +			enable_channel1(1); +			channel1_intr_enable(1);  		}  		mutex_unlock(&common->lock);  	}  	return 0;  } - -static const struct dev_pm_ops vpif_dev_pm_ops = { -	.suspend = vpif_suspend, -	.resume = vpif_resume, -}; - -#define vpif_pm_ops (&vpif_dev_pm_ops) -#else -#define vpif_pm_ops NULL  #endif +static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); +  static __refdata struct platform_driver vpif_driver = {  	.driver	= { -		.name	= "vpif_capture", +		.name	= VPIF_DRIVER_NAME,  		.owner	= THIS_MODULE, -		.pm	= vpif_pm_ops, +		.pm	= &vpif_pm_ops,  	},  	.probe = vpif_probe,  	.remove = vpif_remove, diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 5a29d9a0cae..1ee17824f48 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -19,8 +19,6 @@  #ifndef VPIF_CAPTURE_H  #define VPIF_CAPTURE_H -#ifdef __KERNEL__ -  /* Header files */  #include <media/videobuf2-dma-contig.h>  #include <media/v4l2-device.h> @@ -63,11 +61,6 @@ struct common_obj {  	struct vpif_cap_buffer *cur_frm;  	/* Pointer pointing to current v4l2_buffer */  	struct vpif_cap_buffer *next_frm; -	/* -	 * This field keeps track of type of buffer exchange mechanism -	 * user has selected -	 */ -	enum v4l2_memory memory;  	/* Used to store pixel format */  	struct v4l2_format fmt;  	/* Buffer queue used in video-buf */ @@ -80,10 +73,6 @@ struct common_obj {  	spinlock_t irqlock;  	/* lock used to access this structure */  	struct mutex lock; -	/* number of users performing IO */ -	u32 io_usrs; -	/* Indicates whether streaming started */ -	u8 started;  	/* Function pointer to set the addresses */  	void (*set_addr) (unsigned long, unsigned long, unsigned long,  			  unsigned long); @@ -104,10 +93,6 @@ struct common_obj {  struct channel_obj {  	/* Identifies video device for this channel */  	struct video_device *video_dev; -	/* Used to keep track of state of the priority */ -	struct v4l2_prio_state prio; -	/* number of open instances of the channel */ -	int usrs;  	/* Indicates id of the field which is being displayed */  	u32 field_id;  	/* flag to indicate whether decoder is initialized */ @@ -126,18 +111,6 @@ struct channel_obj {  	struct video_obj video;  }; -/* File handle structure */ -struct vpif_fh { -	/* pointer to channel object for opened device */ -	struct channel_obj *channel; -	/* Indicates whether this file handle is doing IO */ -	u8 io_allowed[VPIF_NUMBER_OF_OBJECTS]; -	/* Used to keep track priority of this instance */ -	enum v4l2_priority prio; -	/* Used to indicate channel is initialize or not */ -	u8 initialized; -}; -  struct vpif_device {  	struct v4l2_device v4l2_dev;  	struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; @@ -157,5 +130,4 @@ struct vpif_config_params {  	u8 max_device_type;  }; -#endif				/* End of __KERNEL__ */  #endif				/* VPIF_CAPTURE_H */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index c31bcf129a5..b431b58f39e 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -3,6 +3,7 @@   * Display driver for TI DaVinci VPIF   *   * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2014 Lad, Prabhakar <prabhakar.csengg@gmail.com>   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License as @@ -35,129 +36,110 @@ MODULE_VERSION(VPIF_DISPLAY_VERSION);  		v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)  static int debug = 1; -static u32 ch2_numbuffers = 3; -static u32 ch3_numbuffers = 3; -static u32 ch2_bufsize = 1920 * 1080 * 2; -static u32 ch3_bufsize = 720 * 576 * 2;  module_param(debug, int, 0644); -module_param(ch2_numbuffers, uint, S_IRUGO); -module_param(ch3_numbuffers, uint, S_IRUGO); -module_param(ch2_bufsize, uint, S_IRUGO); -module_param(ch3_bufsize, uint, S_IRUGO);  MODULE_PARM_DESC(debug, "Debug level 0-1"); -MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); -MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); -MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); -MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); - -static struct vpif_config_params config_params = { -	.min_numbuffers		= 3, -	.numbuffers[0]		= 3, -	.numbuffers[1]		= 3, -	.min_bufsize[0]		= 720 * 480 * 2, -	.min_bufsize[1]		= 720 * 480 * 2, -	.channel_bufsize[0]	= 1920 * 1080 * 2, -	.channel_bufsize[1]	= 720 * 576 * 2, -}; + +#define VPIF_DRIVER_NAME	"vpif_display" + +/* Is set to 1 in case of SDTV formats, 2 in case of HDTV formats. */ +static int ycmux_mode; + +static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };  static struct vpif_device vpif_obj = { {NULL} };  static struct device *vpif_dev;  static void vpif_calculate_offsets(struct channel_obj *ch);  static void vpif_config_addr(struct channel_obj *ch, int muxmode); -/* - * buffer_prepare: This is the callback function called from vb2_qbuf() - * function the buffer is prepared and user space virtual address is converted - * into physical address +static inline struct vpif_disp_buffer *to_vpif_buffer(struct vb2_buffer *vb) +{ +	return container_of(vb, struct vpif_disp_buffer, vb); +} + +/** + * vpif_buffer_prepare :  callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into  physical address   */  static int vpif_buffer_prepare(struct vb2_buffer *vb)  { -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); -	struct vb2_queue *q = vb->vb2_queue; +	struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);  	struct common_obj *common; -	unsigned long addr; - -	common = &fh->channel->common[VPIF_VIDEO_INDEX]; -	if (vb->state != VB2_BUF_STATE_ACTIVE && -		vb->state != VB2_BUF_STATE_PREPARED) { -		vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); -		if (vb2_plane_vaddr(vb, 0) && -		vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) -			goto buf_align_exit; - -		addr = vb2_dma_contig_plane_dma_addr(vb, 0); -		if (q->streaming && -			(V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { -			if (!ISALIGNED(addr + common->ytop_off) || + +	common = &ch->common[VPIF_VIDEO_INDEX]; + +	vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); +	if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) +		return -EINVAL; + +	vb->v4l2_buf.field = common->fmt.fmt.pix.field; + +	if (vb->vb2_queue->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { +		unsigned long addr = vb2_dma_contig_plane_dma_addr(vb, 0); + +		if (!ISALIGNED(addr + common->ytop_off) ||  			!ISALIGNED(addr + common->ybtm_off) ||  			!ISALIGNED(addr + common->ctop_off) || -			!ISALIGNED(addr + common->cbtm_off)) -				goto buf_align_exit; +			!ISALIGNED(addr + common->cbtm_off)) { +			vpif_err("buffer offset not aligned to 8 bytes\n"); +			return -EINVAL;  		}  	} -	return 0; -buf_align_exit: -	vpif_err("buffer offset not aligned to 8 bytes\n"); -	return -EINVAL; +	return 0;  } -/* - * vpif_buffer_queue_setup: This function allocates memory for the buffers +/** + * vpif_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size   */  static int vpif_buffer_queue_setup(struct vb2_queue *vq,  				const struct v4l2_format *fmt,  				unsigned int *nbuffers, unsigned int *nplanes,  				unsigned int sizes[], void *alloc_ctxs[])  { -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	unsigned long size; - -	if (V4L2_MEMORY_MMAP == common->memory) { -		size = config_params.channel_bufsize[ch->channel_id]; -		/* -		* Checking if the buffer size exceeds the available buffer -		* ycmux_mode = 0 means 1 channel mode HD and -		* ycmux_mode = 1 means 2 channels mode SD -		*/ -		if (ch->vpifparams.std_info.ycmux_mode == 0) { -			if (config_params.video_limit[ch->channel_id]) -				while (size * *nbuffers > -					(config_params.video_limit[0] -						+ config_params.video_limit[1])) -					(*nbuffers)--; -		} else { -			if (config_params.video_limit[ch->channel_id]) -				while (size * *nbuffers > -				config_params.video_limit[ch->channel_id]) -					(*nbuffers)--; -		} -	} else { -		size = common->fmt.fmt.pix.sizeimage; -	} -	if (*nbuffers < config_params.min_numbuffers) -			*nbuffers = config_params.min_numbuffers; +	if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage) +		return -EINVAL; + +	if (vq->num_buffers + *nbuffers < 3) +		*nbuffers = 3 - vq->num_buffers;  	*nplanes = 1; -	sizes[0] = size; +	sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;  	alloc_ctxs[0] = common->alloc_ctx; + +	/* Calculate the offset for Y and C data  in the buffer */ +	vpif_calculate_offsets(ch); +  	return 0;  } -/* - * vpif_buffer_queue: This function adds the buffer to DMA queue +/** + * vpif_buffer_queue : Callback function to add buffer to DMA queue + * @vb: ptr to vb2_buffer + * + * This callback fucntion queues the buffer to DMA engine   */  static void vpif_buffer_queue(struct vb2_buffer *vb)  { -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); -	struct vpif_disp_buffer *buf = container_of(vb, -				struct vpif_disp_buffer, vb); -	struct channel_obj *ch = fh->channel; +	struct vpif_disp_buffer *buf = to_vpif_buffer(vb); +	struct channel_obj *ch = vb2_get_drv_priv(vb->vb2_queue);  	struct common_obj *common;  	unsigned long flags; @@ -169,104 +151,26 @@ static void vpif_buffer_queue(struct vb2_buffer *vb)  	spin_unlock_irqrestore(&common->irqlock, flags);  } -/* - * vpif_buf_cleanup: This function is called from the videobuf2 layer to - * free memory allocated to the buffers +/** + * vpif_start_streaming : Starts the DMA engine for streaming + * @vb: ptr to vb2_buffer + * @count: number of buffers   */ -static void vpif_buf_cleanup(struct vb2_buffer *vb) -{ -	struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); -	struct vpif_disp_buffer *buf = container_of(vb, -					struct vpif_disp_buffer, vb); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; -	unsigned long flags; - -	common = &ch->common[VPIF_VIDEO_INDEX]; - -	spin_lock_irqsave(&common->irqlock, flags); -	if (vb->state == VB2_BUF_STATE_ACTIVE) -		list_del_init(&buf->list); -	spin_unlock_irqrestore(&common->irqlock, flags); -} - -static void vpif_wait_prepare(struct vb2_queue *vq) -{ -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; - -	common = &ch->common[VPIF_VIDEO_INDEX]; -	mutex_unlock(&common->lock); -} - -static void vpif_wait_finish(struct vb2_queue *vq) -{ -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; - -	common = &ch->common[VPIF_VIDEO_INDEX]; -	mutex_lock(&common->lock); -} - -static int vpif_buffer_init(struct vb2_buffer *vb) -{ -	struct vpif_disp_buffer *buf = container_of(vb, -					struct vpif_disp_buffer, vb); - -	INIT_LIST_HEAD(&buf->list); - -	return 0; -} - -static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; -  static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  {  	struct vpif_display_config *vpif_config_data =  					vpif_dev->platform_data; -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct vpif_params *vpif = &ch->vpifparams; -	unsigned long addr = 0; -	unsigned long flags; +	struct vpif_disp_buffer *buf, *tmp; +	unsigned long addr, flags;  	int ret; -	/* If buffer queue is empty, return error */  	spin_lock_irqsave(&common->irqlock, flags); -	if (list_empty(&common->dma_queue)) { -		spin_unlock_irqrestore(&common->irqlock, flags); -		vpif_err("buffer queue is empty\n"); -		return -EIO; -	} -	/* Get the next frame from the buffer queue */ -	common->next_frm = common->cur_frm = -			    list_entry(common->dma_queue.next, -				       struct vpif_disp_buffer, list); - -	list_del(&common->cur_frm->list); -	spin_unlock_irqrestore(&common->irqlock, flags); -	/* Mark state of the current frame to active */ -	common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; - -	/* Initialize field_id and started member */ +	/* Initialize field_id */  	ch->field_id = 0; -	common->started = 1; -	addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); -	/* Calculate the offset for Y and C data  in the buffer */ -	vpif_calculate_offsets(ch); - -	if ((ch->vpifparams.std_info.frm_fmt && -		((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) -		&& (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) -		|| (!ch->vpifparams.std_info.frm_fmt -		&& (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { -		vpif_err("conflict in field format and std format\n"); -		return -EINVAL; -	}  	/* clock settings */  	if (vpif_config_data->set_clock) { @@ -274,24 +178,37 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  		ycmux_mode, ch->vpifparams.std_info.hd_sd);  		if (ret < 0) {  			vpif_err("can't set clock\n"); -			return ret; +			goto err;  		}  	}  	/* set the parameters and addresses */  	ret = vpif_set_video_params(vpif, ch->channel_id + 2);  	if (ret < 0) -		return ret; +		goto err; -	common->started = ret; +	ycmux_mode = ret;  	vpif_config_addr(ch, ret); +	/* Get the next frame from the buffer queue */ +	common->next_frm = common->cur_frm = +			    list_entry(common->dma_queue.next, +				       struct vpif_disp_buffer, list); + +	list_del(&common->cur_frm->list); +	spin_unlock_irqrestore(&common->irqlock, flags); +	/* Mark state of the current frame to active */ +	common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + +	addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);  	common->set_addr((addr + common->ytop_off),  			    (addr + common->ybtm_off),  			    (addr + common->ctop_off),  			    (addr + common->cbtm_off)); -	/* Set interrupt for both the fields in VPIF -	    Register enable channel in VPIF register */ +	/* +	 * Set interrupt for both the fields in VPIF +	 * Register enable channel in VPIF register +	 */  	channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;  	if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {  		channel2_intr_assert(); @@ -301,8 +218,7 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  			channel2_clipping_enable(1);  	} -	if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) -		|| (common->started == 2)) { +	if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) {  		channel3_intr_assert();  		channel3_intr_enable(1);  		enable_channel3(1); @@ -311,23 +227,55 @@ static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)  	}  	return 0; + +err: +	list_for_each_entry_safe(buf, tmp, &common->dma_queue, list) { +		list_del(&buf->list); +		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); +	} +	spin_unlock_irqrestore(&common->irqlock, flags); + +	return ret;  } -/* abort streaming and wait for last buffer */ -static int vpif_stop_streaming(struct vb2_queue *vq) +/** + * vpif_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpif_stop_streaming(struct vb2_queue *vq)  { -	struct vpif_fh *fh = vb2_get_drv_priv(vq); -	struct channel_obj *ch = fh->channel; +	struct channel_obj *ch = vb2_get_drv_priv(vq);  	struct common_obj *common;  	unsigned long flags; -	if (!vb2_is_streaming(vq)) -		return 0; -  	common = &ch->common[VPIF_VIDEO_INDEX]; +	/* Disable channel */ +	if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { +		enable_channel2(0); +		channel2_intr_enable(0); +	} +	if (VPIF_CHANNEL3_VIDEO == ch->channel_id || ycmux_mode == 2) { +		enable_channel3(0); +		channel3_intr_enable(0); +	} +  	/* release all active buffers */  	spin_lock_irqsave(&common->irqlock, flags); +	if (common->cur_frm == common->next_frm) { +		vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR); +	} else { +		if (common->cur_frm != NULL) +			vb2_buffer_done(&common->cur_frm->vb, +					VB2_BUF_STATE_ERROR); +		if (common->next_frm != NULL) +			vb2_buffer_done(&common->next_frm->vb, +					VB2_BUF_STATE_ERROR); +	} +  	while (!list_empty(&common->dma_queue)) {  		common->next_frm = list_entry(common->dma_queue.next,  						struct vpif_disp_buffer, list); @@ -335,19 +283,15 @@ static int vpif_stop_streaming(struct vb2_queue *vq)  		vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);  	}  	spin_unlock_irqrestore(&common->irqlock, flags); - -	return 0;  }  static struct vb2_ops video_qops = {  	.queue_setup		= vpif_buffer_queue_setup, -	.wait_prepare		= vpif_wait_prepare, -	.wait_finish		= vpif_wait_finish, -	.buf_init		= vpif_buffer_init, +	.wait_prepare		= vb2_ops_wait_prepare, +	.wait_finish		= vb2_ops_wait_finish,  	.buf_prepare		= vpif_buffer_prepare,  	.start_streaming	= vpif_start_streaming,  	.stop_streaming		= vpif_stop_streaming, -	.buf_cleanup		= vpif_buf_cleanup,  	.buf_queue		= vpif_buffer_queue,  }; @@ -429,8 +373,6 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)  	for (i = 0; i < VPIF_NUMOBJECTS; i++) {  		common = &ch->common[i];  		/* If streaming is started in this channel */ -		if (0 == common->started) -			continue;  		if (1 == ch->vpifparams.std_info.frm_fmt) {  			spin_lock(&common->irqlock); @@ -526,6 +468,7 @@ static int vpif_update_resolution(struct channel_obj *ch)  			return -EINVAL;  	} +	common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;  	common->fmt.fmt.pix.width = std_info->width;  	common->fmt.fmt.pix.height = std_info->height;  	vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", @@ -534,6 +477,17 @@ static int vpif_update_resolution(struct channel_obj *ch)  	/* Set height and width paramateres */  	common->height = std_info->height;  	common->width = std_info->width; +	common->fmt.fmt.pix.sizeimage = common->height * common->width * 2; + +	if (vid_ch->stdid) +		common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; +	else +		common->fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + +	if (ch->vpifparams.std_info.frm_fmt) +		common->fmt.fmt.pix.field = V4L2_FIELD_NONE; +	else +		common->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  	return 0;  } @@ -604,70 +558,6 @@ static void vpif_calculate_offsets(struct channel_obj *ch)  	ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;  } -static void vpif_config_format(struct channel_obj *ch) -{ -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	common->fmt.fmt.pix.field = V4L2_FIELD_ANY; -	if (config_params.numbuffers[ch->channel_id] == 0) -		common->memory = V4L2_MEMORY_USERPTR; -	else -		common->memory = V4L2_MEMORY_MMAP; - -	common->fmt.fmt.pix.sizeimage = -			config_params.channel_bufsize[ch->channel_id]; -	common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; -	common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; -} - -static int vpif_check_format(struct channel_obj *ch, -			     struct v4l2_pix_format *pixfmt) -{ -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	enum v4l2_field field = pixfmt->field; -	u32 sizeimage, hpitch, vpitch; - -	if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) -		goto invalid_fmt_exit; - -	if (!(VPIF_VALID_FIELD(field))) -		goto invalid_fmt_exit; - -	if (pixfmt->bytesperline <= 0) -		goto invalid_pitch_exit; - -	sizeimage = pixfmt->sizeimage; - -	if (vpif_update_resolution(ch)) -		return -EINVAL; - -	hpitch = pixfmt->bytesperline; -	vpitch = sizeimage / (hpitch * 2); - -	/* Check for valid value of pitch */ -	if ((hpitch < ch->vpifparams.std_info.width) || -	    (vpitch < ch->vpifparams.std_info.height)) -		goto invalid_pitch_exit; - -	/* Check for 8 byte alignment */ -	if (!ISALIGNED(hpitch)) { -		vpif_err("invalid pitch alignment\n"); -		return -EINVAL; -	} -	pixfmt->width = common->fmt.fmt.pix.width; -	pixfmt->height = common->fmt.fmt.pix.height; - -	return 0; - -invalid_fmt_exit: -	vpif_err("invalid field format\n"); -	return -EINVAL; - -invalid_pitch_exit: -	vpif_err("invalid pitch\n"); -	return -EINVAL; -} -  static void vpif_config_addr(struct channel_obj *ch, int muxmode)  {  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; @@ -682,139 +572,6 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode)  	}  } -/* - * vpif_mmap: It is used to map kernel space buffers into user spaces - */ -static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) -{ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); -	int ret; - -	vpif_dbg(2, debug, "vpif_mmap\n"); - -	if (mutex_lock_interruptible(&common->lock)) -		return -ERESTARTSYS; -	ret = vb2_mmap(&common->buffer_queue, vma); -	mutex_unlock(&common->lock); -	return ret; -} - -/* - * vpif_poll: It is used for select/poll system call - */ -static unsigned int vpif_poll(struct file *filep, poll_table *wait) -{ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	unsigned int res = 0; - -	if (common->started) { -		mutex_lock(&common->lock); -		res = vb2_poll(&common->buffer_queue, filep, wait); -		mutex_unlock(&common->lock); -	} - -	return res; -} - -/* - * vpif_open: It creates object of file handle structure and stores it in - * private_data member of filepointer - */ -static int vpif_open(struct file *filep) -{ -	struct video_device *vdev = video_devdata(filep); -	struct channel_obj *ch = video_get_drvdata(vdev); -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	struct vpif_fh *fh; - -	/* Allocate memory for the file handle object */ -	fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); -	if (fh == NULL) { -		vpif_err("unable to allocate memory for file handle object\n"); -		return -ENOMEM; -	} - -	if (mutex_lock_interruptible(&common->lock)) { -		kfree(fh); -		return -ERESTARTSYS; -	} -	/* store pointer to fh in private_data member of filep */ -	filep->private_data = fh; -	fh->channel = ch; -	fh->initialized = 0; -	if (!ch->initialized) { -		fh->initialized = 1; -		ch->initialized = 1; -		memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); -	} - -	/* Increment channel usrs counter */ -	atomic_inc(&ch->usrs); -	/* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ -	fh->io_allowed[VPIF_VIDEO_INDEX] = 0; -	/* Initialize priority of this instance to default priority */ -	fh->prio = V4L2_PRIORITY_UNSET; -	v4l2_prio_open(&ch->prio, &fh->prio); -	mutex_unlock(&common->lock); - -	return 0; -} - -/* - * vpif_release: This function deletes buffer queue, frees the buffers and - * the vpif file handle - */ -static int vpif_release(struct file *filep) -{ -	struct vpif_fh *fh = filep->private_data; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	mutex_lock(&common->lock); -	/* if this instance is doing IO */ -	if (fh->io_allowed[VPIF_VIDEO_INDEX]) { -		/* Reset io_usrs member of channel object */ -		common->io_usrs = 0; -		/* Disable channel */ -		if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { -			enable_channel2(0); -			channel2_intr_enable(0); -		} -		if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || -		    (2 == common->started)) { -			enable_channel3(0); -			channel3_intr_enable(0); -		} -		common->started = 0; - -		/* Free buffers allocated */ -		vb2_queue_release(&common->buffer_queue); -		vb2_dma_contig_cleanup_ctx(common->alloc_ctx); - -		common->numbuffers = -		    config_params.numbuffers[ch->channel_id]; -	} - -	/* Decrement channel usrs counter */ -	atomic_dec(&ch->usrs); -	/* If this file handle has initialize encoder device, reset it */ -	if (fh->initialized) -		ch->initialized = 0; - -	/* Close the priority */ -	v4l2_prio_close(&ch->prio, fh->prio); -	filep->private_data = NULL; -	fh->initialized = 0; -	mutex_unlock(&common->lock); -	kfree(fh); - -	return 0; -} -  /* functions implementing ioctls */  /**   * vpif_querycap() - QUERYCAP handler @@ -829,7 +586,7 @@ static int vpif_querycap(struct file *file, void  *priv,  	cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;  	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -	snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); +	strlcpy(cap->driver, VPIF_DRIVER_NAME, sizeof(cap->driver));  	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",  		 dev_name(vpif_dev));  	strlcpy(cap->card, config->card_name, sizeof(cap->card)); @@ -840,24 +597,22 @@ static int vpif_querycap(struct file *file, void  *priv,  static int vpif_enum_fmt_vid_out(struct file *file, void  *priv,  					struct v4l2_fmtdesc *fmt)  { -	if (fmt->index != 0) { -		vpif_err("Invalid format index\n"); +	if (fmt->index != 0)  		return -EINVAL; -	}  	/* Fill in the information about format */  	fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;  	strcpy(fmt->description, "YCbCr4:2:2 YC Planar");  	fmt->pixelformat = V4L2_PIX_FMT_YUV422P; - +	fmt->flags = 0;  	return 0;  }  static int vpif_g_fmt_vid_out(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	/* Check the validity of the buffer type */ @@ -870,192 +625,84 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv,  	return 0;  } -static int vpif_s_fmt_vid_out(struct file *file, void *priv, +static int vpif_try_fmt_vid_out(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct v4l2_pix_format *pixfmt; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	int ret = 0; - -	if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) -	    || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_dbg(1, debug, "Channel Busy\n"); -			return -EBUSY; -		} +	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; -		/* Check for the priority */ -		ret = v4l2_prio_check(&ch->prio, fh->prio); -		if (0 != ret) -			return ret; -		fh->initialized = 1; -	} +	/* +	 * to supress v4l-compliance warnings silently correct +	 * the pixelformat +	 */ +	if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) +		pixfmt->pixelformat = common->fmt.fmt.pix.pixelformat; -	if (common->started) { -		vpif_dbg(1, debug, "Streaming in progress\n"); -		return -EBUSY; -	} +	if (vpif_update_resolution(ch)) +		return -EINVAL; -	pixfmt = &fmt->fmt.pix; -	/* Check for valid field format */ -	ret = vpif_check_format(ch, pixfmt); -	if (ret) -		return ret; +	pixfmt->colorspace = common->fmt.fmt.pix.colorspace; +	pixfmt->field = common->fmt.fmt.pix.field; +	pixfmt->bytesperline = common->fmt.fmt.pix.width; +	pixfmt->width = common->fmt.fmt.pix.width; +	pixfmt->height = common->fmt.fmt.pix.height; +	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height * 2; +	pixfmt->priv = 0; -	/* store the pix format in the channel object */ -	common->fmt.fmt.pix = *pixfmt; -	/* store the format in the channel object */ -	common->fmt = *fmt;  	return 0;  } -static int vpif_try_fmt_vid_out(struct file *file, void *priv, +static int vpif_s_fmt_vid_out(struct file *file, void *priv,  				struct v4l2_format *fmt)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; -	int ret = 0; - -	ret = vpif_check_format(ch, pixfmt); -	if (ret) { -		*pixfmt = common->fmt.fmt.pix; -		pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; -	} - -	return ret; -} - -static int vpif_reqbufs(struct file *file, void *priv, -			struct v4l2_requestbuffers *reqbuf) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common; -	enum v4l2_field field; -	struct vb2_queue *q; -	u8 index = 0;  	int ret; -	/* This file handle has not initialized the channel, -	   It is not allowed to do settings */ -	if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) -	    || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { -		if (!fh->initialized) { -			vpif_err("Channel Busy\n"); -			return -EBUSY; -		} -	} - -	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) -		return -EINVAL; - -	index = VPIF_VIDEO_INDEX; - -	common = &ch->common[index]; - -	if (common->fmt.type != reqbuf->type || !vpif_dev) -		return -EINVAL; -	if (0 != common->io_usrs) +	if (vb2_is_busy(&common->buffer_queue))  		return -EBUSY; -	if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { -		if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) -			field = V4L2_FIELD_INTERLACED; -		else -			field = common->fmt.fmt.pix.field; -	} else { -		field = V4L2_VBI_INTERLACED; -	} -	/* Initialize videobuf2 queue as per the buffer type */ -	common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); -	if (IS_ERR(common->alloc_ctx)) { -		vpif_err("Failed to get the context\n"); -		return PTR_ERR(common->alloc_ctx); -	} -	q = &common->buffer_queue; -	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; -	q->io_modes = VB2_MMAP | VB2_USERPTR; -	q->drv_priv = fh; -	q->ops = &video_qops; -	q->mem_ops = &vb2_dma_contig_memops; -	q->buf_struct_size = sizeof(struct vpif_disp_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - -	ret = vb2_queue_init(q); -	if (ret) { -		vpif_err("vpif_display: vb2_queue_init() failed\n"); -		vb2_dma_contig_cleanup_ctx(common->alloc_ctx); +	ret = vpif_try_fmt_vid_out(file, priv, fmt); +	if (ret)  		return ret; -	} -	/* Set io allowed member of file handle to TRUE */ -	fh->io_allowed[index] = 1; -	/* Increment io usrs member of channel object to 1 */ -	common->io_usrs = 1; -	/* Store type of memory requested in channel object */ -	common->memory = reqbuf->memory; -	INIT_LIST_HEAD(&common->dma_queue); -	/* Allocate buffers */ -	return vb2_reqbufs(&common->buffer_queue, reqbuf); -} -static int vpif_querybuf(struct file *file, void *priv, -				struct v4l2_buffer *tbuf) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	if (common->fmt.type != tbuf->type) -		return -EINVAL; +	/* store the pix format in the channel object */ +	common->fmt.fmt.pix = *pixfmt; -	return vb2_querybuf(&common->buffer_queue, tbuf); +	/* store the format in the channel object */ +	common->fmt = *fmt; +	return 0;  } -static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  { -	struct vpif_fh *fh = NULL; -	struct channel_obj *ch = NULL; -	struct common_obj *common = NULL; - -	if (!buf || !priv) -		return -EINVAL; - -	fh = priv; -	ch = fh->channel; -	if (!ch) -		return -EINVAL; +	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; +	struct vpif_display_chan_config *chan_cfg; +	struct v4l2_output output; +	int ret; -	common = &(ch->common[VPIF_VIDEO_INDEX]); -	if (common->fmt.type != buf->type) -		return -EINVAL; +	if (config->chan_config[ch->channel_id].outputs == NULL) +		return -ENODATA; -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_err("fh->io_allowed\n"); -		return -EACCES; -	} +	chan_cfg = &config->chan_config[ch->channel_id]; +	output = chan_cfg->outputs[ch->output_idx].output; +	if (output.capabilities != V4L2_OUT_CAP_STD) +		return -ENODATA; -	return vb2_qbuf(&common->buffer_queue, buf); -} +	if (vb2_is_busy(&common->buffer_queue)) +		return -EBUSY; -static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	int ret = 0;  	if (!(std_id & VPIF_V4L2_STD))  		return -EINVAL; -	if (common->started) { -		vpif_err("streaming in progress\n"); -		return -EBUSY; -	} -  	/* Call encoder subdevice function to set the standard */  	ch->video.stdid = std_id;  	memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); @@ -1063,16 +710,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  	if (vpif_update_resolution(ch))  		return -EINVAL; -	if ((ch->vpifparams.std_info.width * -		ch->vpifparams.std_info.height * 2) > -		config_params.channel_bufsize[ch->channel_id]) { -		vpif_err("invalid std for this size\n"); -		return -EINVAL; -	} -  	common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; -	/* Configure the default format information */ -	vpif_config_format(ch);  	ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,  						s_std_output, std_id); @@ -1081,7 +719,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  		return ret;  	} -	ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, +	ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,  							s_std, std_id);  	if (ret < 0)  		vpif_err("Failed to set standard for sub devices\n"); @@ -1090,132 +728,21 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id std_id)  static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; - -	*std = ch->video.stdid; -	return 0; -} - -static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - -	return vb2_dqbuf(&common->buffer_queue, p, -					(file->f_flags & O_NONBLOCK)); -} - -static int vpif_streamon(struct file *file, void *priv, -				enum v4l2_buf_type buftype) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; -	int ret = 0; - -	if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { -		vpif_err("buffer type not supported\n"); -		return -EINVAL; -	} - -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_err("fh->io_allowed\n"); -		return -EACCES; -	} - -	/* If Streaming is already started, return error */ -	if (common->started) { -		vpif_err("channel->started\n"); -		return -EBUSY; -	} - -	if ((ch->channel_id == VPIF_CHANNEL2_VIDEO -		&& oth_ch->common[VPIF_VIDEO_INDEX].started && -		ch->vpifparams.std_info.ycmux_mode == 0) -		|| ((ch->channel_id == VPIF_CHANNEL3_VIDEO) -		&& (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { -		vpif_err("other channel is using\n"); -		return -EBUSY; -	} - -	ret = vpif_check_format(ch, &common->fmt.fmt.pix); -	if (ret < 0) -		return ret; - -	/* Call vb2_streamon to start streaming in videobuf2 */ -	ret = vb2_streamon(&common->buffer_queue, buftype); -	if (ret < 0) { -		vpif_err("vb2_streamon\n"); -		return ret; -	} - -	return ret; -} - -static int vpif_streamoff(struct file *file, void *priv, -				enum v4l2_buf_type buftype) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	struct vpif_display_config *vpif_config_data = -					vpif_dev->platform_data; - -	if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { -		vpif_err("buffer type not supported\n"); -		return -EINVAL; -	} - -	if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { -		vpif_err("fh->io_allowed\n"); -		return -EACCES; -	} - -	if (!common->started) { -		vpif_err("channel->started\n"); -		return -EINVAL; -	} - -	if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { -		/* disable channel */ -		if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { -			if (vpif_config_data-> -				chan_config[VPIF_CHANNEL2_VIDEO].clip_en) -				channel2_clipping_enable(0); -			enable_channel2(0); -			channel2_intr_enable(0); -		} -		if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || -					(2 == common->started)) { -			if (vpif_config_data-> -				chan_config[VPIF_CHANNEL3_VIDEO].clip_en) -				channel3_clipping_enable(0); -			enable_channel3(0); -			channel3_intr_enable(0); -		} -	} - -	common->started = 0; -	return vb2_streamoff(&common->buffer_queue, buftype); -} +	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_display_chan_config *chan_cfg; +	struct v4l2_output output; -static int vpif_cropcap(struct file *file, void *priv, -			struct v4l2_cropcap *crop) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; -	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; -	if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) -		return -EINVAL; +	if (config->chan_config[ch->channel_id].outputs == NULL) +		return -ENODATA; -	crop->bounds.left = crop->bounds.top = 0; -	crop->defrect.left = crop->defrect.top = 0; -	crop->defrect.height = crop->bounds.height = common->height; -	crop->defrect.width = crop->bounds.width = common->width; +	chan_cfg = &config->chan_config[ch->channel_id]; +	output = chan_cfg->outputs[ch->output_idx].output; +	if (output.capabilities != V4L2_OUT_CAP_STD) +		return -ENODATA; +	*std = ch->video.stdid;  	return 0;  } @@ -1224,9 +751,9 @@ static int vpif_enum_output(struct file *file, void *fh,  {  	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct vpif_display_chan_config *chan_cfg; -	struct vpif_fh *vpif_handler = fh; -	struct channel_obj *ch = vpif_handler->channel;  	chan_cfg = &config->chan_config[ch->channel_id];  	if (output->index >= chan_cfg->output_count) { @@ -1320,52 +847,32 @@ static int vpif_set_output(struct vpif_display_config *vpif_cfg,  static int vpif_s_output(struct file *file, void *priv, unsigned int i)  {  	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct vpif_display_chan_config *chan_cfg; -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel;  	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; +	if (vb2_is_busy(&common->buffer_queue)) +		return -EBUSY; +  	chan_cfg = &config->chan_config[ch->channel_id];  	if (i >= chan_cfg->output_count)  		return -EINVAL; -	if (common->started) { -		vpif_err("Streaming in progress\n"); -		return -EBUSY; -	} -  	return vpif_set_output(config, ch, i);  }  static int vpif_g_output(struct file *file, void *priv, unsigned int *i)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	*i = ch->output_idx;  	return 0;  } -static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; - -	*p = v4l2_prio_max(&ch->prio); - -	return 0; -} - -static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) -{ -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; - -	return v4l2_prio_change(&ch->prio, &fh->prio, p); -} -  /**   * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler   * @file: file ptr @@ -1376,11 +883,24 @@ static int  vpif_enum_dv_timings(struct file *file, void *priv,  		     struct v4l2_enum_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_display_chan_config *chan_cfg; +	struct v4l2_output output;  	int ret; -	ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); +	if (config->chan_config[ch->channel_id].outputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	output = chan_cfg->outputs[ch->output_idx].output; +	if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) +		return -ENODATA; + +	timings->pad = 0; + +	ret = v4l2_subdev_call(ch->sd, pad, enum_dv_timings, timings);  	if (ret == -ENOIOCTLCMD || ret == -ENODEV)  		return -EINVAL;  	return ret; @@ -1395,14 +915,29 @@ vpif_enum_dv_timings(struct file *file, void *priv,  static int vpif_s_dv_timings(struct file *file, void *priv,  		struct v4l2_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev);  	struct vpif_params *vpifparams = &ch->vpifparams; +	struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];  	struct vpif_channel_config_params *std_info = &vpifparams->std_info;  	struct video_obj *vid_ch = &ch->video;  	struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; +	struct vpif_display_chan_config *chan_cfg; +	struct v4l2_output output;  	int ret; +	if (config->chan_config[ch->channel_id].outputs == NULL) +		return -ENODATA; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	output = chan_cfg->outputs[ch->output_idx].output; +	if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) +		return -ENODATA; + +	if (vb2_is_busy(&common->buffer_queue)) +		return -EBUSY; +  	if (timings->type != V4L2_DV_BT_656_1120) {  		vpif_dbg(2, debug, "Timing type not defined\n");  		return -EINVAL; @@ -1484,13 +1019,27 @@ static int vpif_s_dv_timings(struct file *file, void *priv,  static int vpif_g_dv_timings(struct file *file, void *priv,  		struct v4l2_dv_timings *timings)  { -	struct vpif_fh *fh = priv; -	struct channel_obj *ch = fh->channel; +	struct vpif_display_config *config = vpif_dev->platform_data; +	struct video_device *vdev = video_devdata(file); +	struct channel_obj *ch = video_get_drvdata(vdev); +	struct vpif_display_chan_config *chan_cfg;  	struct video_obj *vid_ch = &ch->video; +	struct v4l2_output output; + +	if (config->chan_config[ch->channel_id].outputs == NULL) +		goto error; + +	chan_cfg = &config->chan_config[ch->channel_id]; +	output = chan_cfg->outputs[ch->output_idx].output; + +	if (output.capabilities != V4L2_OUT_CAP_DV_TIMINGS) +		goto error;  	*timings = vid_ch->dv_timings;  	return 0; +error: +	return -ENODATA;  }  /* @@ -1510,83 +1059,49 @@ static int vpif_log_status(struct file *filep, void *priv)  /* vpif display ioctl operations */  static const struct v4l2_ioctl_ops vpif_ioctl_ops = { -	.vidioc_querycap        	= vpif_querycap, -	.vidioc_g_priority		= vpif_g_priority, -	.vidioc_s_priority		= vpif_s_priority, +	.vidioc_querycap		= vpif_querycap,  	.vidioc_enum_fmt_vid_out	= vpif_enum_fmt_vid_out, -	.vidioc_g_fmt_vid_out  		= vpif_g_fmt_vid_out, -	.vidioc_s_fmt_vid_out   	= vpif_s_fmt_vid_out, -	.vidioc_try_fmt_vid_out 	= vpif_try_fmt_vid_out, -	.vidioc_reqbufs         	= vpif_reqbufs, -	.vidioc_querybuf        	= vpif_querybuf, -	.vidioc_qbuf            	= vpif_qbuf, -	.vidioc_dqbuf           	= vpif_dqbuf, -	.vidioc_streamon        	= vpif_streamon, -	.vidioc_streamoff       	= vpif_streamoff, -	.vidioc_s_std           	= vpif_s_std, +	.vidioc_g_fmt_vid_out		= vpif_g_fmt_vid_out, +	.vidioc_s_fmt_vid_out		= vpif_s_fmt_vid_out, +	.vidioc_try_fmt_vid_out		= vpif_try_fmt_vid_out, + +	.vidioc_reqbufs			= vb2_ioctl_reqbufs, +	.vidioc_create_bufs		= vb2_ioctl_create_bufs, +	.vidioc_querybuf		= vb2_ioctl_querybuf, +	.vidioc_qbuf			= vb2_ioctl_qbuf, +	.vidioc_dqbuf			= vb2_ioctl_dqbuf, +	.vidioc_expbuf			= vb2_ioctl_expbuf, +	.vidioc_streamon		= vb2_ioctl_streamon, +	.vidioc_streamoff		= vb2_ioctl_streamoff, + +	.vidioc_s_std			= vpif_s_std,  	.vidioc_g_std			= vpif_g_std, +  	.vidioc_enum_output		= vpif_enum_output,  	.vidioc_s_output		= vpif_s_output,  	.vidioc_g_output		= vpif_g_output, -	.vidioc_cropcap         	= vpif_cropcap, -	.vidioc_enum_dv_timings         = vpif_enum_dv_timings, -	.vidioc_s_dv_timings            = vpif_s_dv_timings, -	.vidioc_g_dv_timings            = vpif_g_dv_timings, + +	.vidioc_enum_dv_timings		= vpif_enum_dv_timings, +	.vidioc_s_dv_timings		= vpif_s_dv_timings, +	.vidioc_g_dv_timings		= vpif_g_dv_timings, +  	.vidioc_log_status		= vpif_log_status,  };  static const struct v4l2_file_operations vpif_fops = {  	.owner		= THIS_MODULE, -	.open		= vpif_open, -	.release	= vpif_release, +	.open		= v4l2_fh_open, +	.release	= vb2_fop_release,  	.unlocked_ioctl	= video_ioctl2, -	.mmap		= vpif_mmap, -	.poll		= vpif_poll -}; - -static struct video_device vpif_video_template = { -	.name		= "vpif", -	.fops		= &vpif_fops, -	.ioctl_ops	= &vpif_ioctl_ops, +	.mmap		= vb2_fop_mmap, +	.poll		= vb2_fop_poll  };  /*Configure the channels, buffer sizei, request irq */  static int initialize_vpif(void)  {  	int free_channel_objects_index; -	int free_buffer_channel_index; -	int free_buffer_index; -	int err = 0, i, j; - -	/* Default number of buffers should be 3 */ -	if ((ch2_numbuffers > 0) && -	    (ch2_numbuffers < config_params.min_numbuffers)) -		ch2_numbuffers = config_params.min_numbuffers; -	if ((ch3_numbuffers > 0) && -	    (ch3_numbuffers < config_params.min_numbuffers)) -		ch3_numbuffers = config_params.min_numbuffers; - -	/* Set buffer size to min buffers size if invalid buffer size is -	 * given */ -	if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) -		ch2_bufsize = -		    config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; -	if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) -		ch3_bufsize = -		    config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; - -	config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; - -	if (ch2_numbuffers) { -		config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = -							ch2_bufsize; -	} -	config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; - -	if (ch3_numbuffers) { -		config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = -							ch3_bufsize; -	} +	int err, i, j;  	/* Allocate memory for six channel objects */  	for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { @@ -1600,10 +1115,6 @@ static int initialize_vpif(void)  		}  	} -	free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; -	free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; -	free_buffer_index = config_params.numbuffers[i - 1]; -  	return 0;  vpif_init_free_channel_objects: @@ -1632,21 +1143,18 @@ static int vpif_async_bound(struct v4l2_async_notifier *notifier,  static int vpif_probe_complete(void)  {  	struct common_obj *common; +	struct video_device *vdev;  	struct channel_obj *ch; +	struct vb2_queue *q;  	int j, err, k;  	for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {  		ch = vpif_obj.dev[j];  		/* Initialize field of the channel objects */ -		atomic_set(&ch->usrs, 0);  		for (k = 0; k < VPIF_NUMOBJECTS; k++) { -			ch->common[k].numbuffers = 0;  			common = &ch->common[k]; -			common->io_usrs = 0; -			common->started = 0;  			spin_lock_init(&common->irqlock);  			mutex_init(&common->lock); -			common->numbuffers = 0;  			common->set_addr = NULL;  			common->ytop_off = 0;  			common->ybtm_off = 0; @@ -1655,38 +1163,71 @@ static int vpif_probe_complete(void)  			common->cur_frm = NULL;  			common->next_frm = NULL;  			memset(&common->fmt, 0, sizeof(common->fmt)); -			common->numbuffers = config_params.numbuffers[k];  		}  		ch->initialized = 0;  		if (vpif_obj.config->subdev_count)  			ch->sd = vpif_obj.sd[0];  		ch->channel_id = j; -		if (j < 2) -			ch->common[VPIF_VIDEO_INDEX].numbuffers = -			    config_params.numbuffers[ch->channel_id]; -		else -			ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;  		memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); -		/* Initialize prio member of channel object */ -		v4l2_prio_init(&ch->prio);  		ch->common[VPIF_VIDEO_INDEX].fmt.type =  						V4L2_BUF_TYPE_VIDEO_OUTPUT; -		ch->video_dev->lock = &common->lock; -		video_set_drvdata(ch->video_dev, ch);  		/* select output 0 */  		err = vpif_set_output(vpif_obj.config, ch, 0);  		if (err)  			goto probe_out; +		/* set initial format */ +		ch->video.stdid = V4L2_STD_525_60; +		memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); +		vpif_update_resolution(ch); + +		/* Initialize vb2 queue */ +		q = &common->buffer_queue; +		q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; +		q->drv_priv = ch; +		q->ops = &video_qops; +		q->mem_ops = &vb2_dma_contig_memops; +		q->buf_struct_size = sizeof(struct vpif_disp_buffer); +		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +		q->min_buffers_needed = 1; +		q->lock = &common->lock; +		err = vb2_queue_init(q); +		if (err) { +			vpif_err("vpif_display: vb2_queue_init() failed\n"); +			goto probe_out; +		} + +		common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); +		if (IS_ERR(common->alloc_ctx)) { +			vpif_err("Failed to get the context\n"); +			err = PTR_ERR(common->alloc_ctx); +			goto probe_out; +		} + +		INIT_LIST_HEAD(&common->dma_queue); +  		/* register video device */  		vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",  			 (int)ch, (int)&ch->video_dev); -		err = video_register_device(ch->video_dev, -					  VFL_TYPE_GRABBER, (j ? 3 : 2)); +		/* Initialize the video_device structure */ +		vdev = ch->video_dev; +		strlcpy(vdev->name, VPIF_DRIVER_NAME, sizeof(vdev->name)); +		vdev->release = video_device_release; +		vdev->fops = &vpif_fops; +		vdev->ioctl_ops = &vpif_ioctl_ops; +		vdev->v4l2_dev = &vpif_obj.v4l2_dev; +		vdev->vfl_dir = VFL_DIR_TX; +		vdev->queue = q; +		vdev->lock = &common->lock; +		set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); +		video_set_drvdata(ch->video_dev, ch); +		err = video_register_device(vdev, VFL_TYPE_GRABBER, +					    (j ? 3 : 2));  		if (err < 0)  			goto probe_out;  	} @@ -1696,6 +1237,8 @@ static int vpif_probe_complete(void)  probe_out:  	for (k = 0; k < j; k++) {  		ch = vpif_obj.dev[k]; +		common = &ch->common[k]; +		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);  		video_unregister_device(ch->video_dev);  		video_device_release(ch->video_dev);  		ch->video_dev = NULL; @@ -1722,7 +1265,6 @@ static __init int vpif_probe(struct platform_device *pdev)  	struct video_device *vfd;  	struct resource *res;  	int subdev_count; -	size_t size;  	vpif_dev = &pdev->dev;  	err = initialize_vpif(); @@ -1740,7 +1282,7 @@ static __init int vpif_probe(struct platform_device *pdev)  	while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {  		err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, -					IRQF_SHARED, "VPIF_Display", +					IRQF_SHARED, VPIF_DRIVER_NAME,  					(void *)(&vpif_obj.dev[res_idx]->  					channel_id));  		if (err) { @@ -1766,36 +1308,10 @@ static __init int vpif_probe(struct platform_device *pdev)  			goto vpif_unregister;  		} -		/* Initialize field of video device */ -		*vfd = vpif_video_template; -		vfd->v4l2_dev = &vpif_obj.v4l2_dev; -		vfd->release = video_device_release; -		vfd->vfl_dir = VFL_DIR_TX; -		snprintf(vfd->name, sizeof(vfd->name), -			 "VPIF_Display_DRIVER_V%s", -			 VPIF_DISPLAY_VERSION); -  		/* Set video_dev to the video device */  		ch->video_dev = vfd;  	} -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (res) { -		size = resource_size(res); -		/* The resources are divided into two equal memory and when -		 * we have HD output we can add them together -		 */ -		for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { -			ch = vpif_obj.dev[j]; -			ch->channel_id = j; - -			/* only enabled if second resource exists */ -			config_params.video_limit[ch->channel_id] = 0; -			if (size) -				config_params.video_limit[ch->channel_id] = -									size/2; -		} -	}  	vpif_obj.config = pdev->dev.platform_data;  	subdev_count = vpif_obj.config->subdev_count;  	subdevdata = vpif_obj.config->subdevinfo; @@ -1861,6 +1377,7 @@ vpif_unregister:   */  static int vpif_remove(struct platform_device *device)  { +	struct common_obj *common;  	struct channel_obj *ch;  	int i; @@ -1871,6 +1388,8 @@ static int vpif_remove(struct platform_device *device)  	for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i]; +		common = &ch->common[i]; +		vb2_dma_contig_cleanup_ctx(common->alloc_ctx);  		/* Unregister video device */  		video_unregister_device(ch->video_dev); @@ -1881,7 +1400,7 @@ static int vpif_remove(struct platform_device *device)  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int vpif_suspend(struct device *dev)  {  	struct common_obj *common; @@ -1892,18 +1411,20 @@ static int vpif_suspend(struct device *dev)  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i];  		common = &ch->common[VPIF_VIDEO_INDEX]; + +		if (!vb2_is_streaming(&common->buffer_queue)) +			continue; +  		mutex_lock(&common->lock); -		if (atomic_read(&ch->usrs) && common->io_usrs) { -			/* Disable channel */ -			if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { -				enable_channel2(0); -				channel2_intr_enable(0); -			} -			if (ch->channel_id == VPIF_CHANNEL3_VIDEO || -					common->started == 2) { -				enable_channel3(0); -				channel3_intr_enable(0); -			} +		/* Disable channel */ +		if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { +			enable_channel2(0); +			channel2_intr_enable(0); +		} +		if (ch->channel_id == VPIF_CHANNEL3_VIDEO || +			ycmux_mode == 2) { +			enable_channel3(0); +			channel3_intr_enable(0);  		}  		mutex_unlock(&common->lock);  	} @@ -1922,18 +1443,20 @@ static int vpif_resume(struct device *dev)  		/* Get the pointer to the channel object */  		ch = vpif_obj.dev[i];  		common = &ch->common[VPIF_VIDEO_INDEX]; + +		if (!vb2_is_streaming(&common->buffer_queue)) +			continue; +  		mutex_lock(&common->lock); -		if (atomic_read(&ch->usrs) && common->io_usrs) { -			/* Enable channel */ -			if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { -				enable_channel2(1); -				channel2_intr_enable(1); -			} -			if (ch->channel_id == VPIF_CHANNEL3_VIDEO || -					common->started == 2) { -				enable_channel3(1); -				channel3_intr_enable(1); -			} +		/* Enable channel */ +		if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { +			enable_channel2(1); +			channel2_intr_enable(1); +		} +		if (ch->channel_id == VPIF_CHANNEL3_VIDEO || +				ycmux_mode == 2) { +			enable_channel3(1); +			channel3_intr_enable(1);  		}  		mutex_unlock(&common->lock);  	} @@ -1941,21 +1464,15 @@ static int vpif_resume(struct device *dev)  	return 0;  } -static const struct dev_pm_ops vpif_pm = { -	.suspend        = vpif_suspend, -	.resume         = vpif_resume, -}; - -#define vpif_pm_ops (&vpif_pm) -#else -#define vpif_pm_ops NULL  #endif +static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume); +  static __refdata struct platform_driver vpif_driver = {  	.driver	= { -			.name	= "vpif_display", +			.name	= VPIF_DRIVER_NAME,  			.owner	= THIS_MODULE, -			.pm	= vpif_pm_ops, +			.pm	= &vpif_pm_ops,  	},  	.probe	= vpif_probe,  	.remove	= vpif_remove, diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index 4d0485b99a8..7b21a760767 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -13,8 +13,8 @@   * GNU General Public License for more details.   */ -#ifndef DAVINCIHD_DISPLAY_H -#define DAVINCIHD_DISPLAY_H +#ifndef VPIF_DISPLAY_H +#define VPIF_DISPLAY_H  /* Header files */  #include <media/videobuf2-dma-contig.h> @@ -67,17 +67,10 @@ struct vpif_disp_buffer {  };  struct common_obj { -	/* Buffer specific parameters */ -	u8 *fbuffers[VIDEO_MAX_FRAME];		/* List of buffer pointers for -						 * storing frames */ -	u32 numbuffers;				/* number of buffers */  	struct vpif_disp_buffer *cur_frm;	/* Pointer pointing to current  						 * vb2_buffer */  	struct vpif_disp_buffer *next_frm;	/* Pointer pointing to next  						 * vb2_buffer */ -	enum v4l2_memory memory;		/* This field keeps track of -						 * type of buffer exchange -						 * method user has selected */  	struct v4l2_format fmt;			/* Used to store the format */  	struct vb2_queue buffer_queue;		/* Buffer queue used in  						 * video-buf */ @@ -90,10 +83,6 @@ struct common_obj {  	/* channel specific parameters */  	struct mutex lock;			/* lock used to access this  						 * structure */ -	u32 io_usrs;				/* number of users performing -						 * IO */ -	u8 started;				/* Indicates whether streaming -						 * started */  	u32 ytop_off;				/* offset of Y top from the  						 * starting of the buffer */  	u32 ybtm_off;				/* offset of Y bottom from the @@ -103,7 +92,7 @@ struct common_obj {  	u32 cbtm_off;				/* offset of C bottom from the  						 * starting of the buffer */  	/* Function pointer to set the addresses */ -	void (*set_addr) (unsigned long, unsigned long, +	void (*set_addr)(unsigned long, unsigned long,  				unsigned long, unsigned long);  	u32 height;  	u32 width; @@ -113,10 +102,6 @@ struct channel_obj {  	/* V4l2 specific parameters */  	struct video_device *video_dev;	/* Identifies video device for  					 * this channel */ -	struct v4l2_prio_state prio;	/* Used to keep track of state of -					 * the priority */ -	atomic_t usrs;			/* number of open instances of -					 * the channel */  	u32 field_id;			/* Indicates id of the field  					 * which is being displayed */  	u8 initialized;			/* flag to indicate whether @@ -130,19 +115,6 @@ struct channel_obj {  	struct video_obj video;  }; -/* File handle structure */ -struct vpif_fh { -	struct channel_obj *channel;	/* pointer to channel object for -					 * opened device */ -	u8 io_allowed[VPIF_NUMOBJECTS];	/* Indicates whether this file handle -					 * is doing IO */ -	enum v4l2_priority prio;	/* Used to keep track priority of -					 * this instance */ -	u8 initialized;			/* Used to keep track of whether this -					 * file handle has initialized -					 * channel or not */ -}; -  /* vpif device structure */  struct vpif_device {  	struct v4l2_device v4l2_dev; @@ -152,12 +124,4 @@ struct vpif_device {  	struct vpif_display_config *config;  }; -struct vpif_config_params { -	u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; -	u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; -	u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; -	u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS]; -	u8 min_numbuffers; -}; - -#endif				/* DAVINCIHD_DISPLAY_H */ +#endif				/* VPIF_DISPLAY_H */ diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 76435d3bf62..ef0a6564cef 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -45,6 +45,7 @@  #define GSC_DST_FMT			(1 << 2)  #define GSC_CTX_M2M			(1 << 3)  #define GSC_CTX_STOP_REQ		(1 << 6) +#define	GSC_CTX_ABORT			(1 << 7)  enum gsc_dev_flags {  	/* for global */ diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e576ff2de3d..e434f1f03d7 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -46,6 +46,17 @@ static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)  	return ret == 0 ? -ETIMEDOUT : ret;  } +static void __gsc_m2m_job_abort(struct gsc_ctx *ctx) +{ +	int ret; + +	ret = gsc_m2m_ctx_stop_req(ctx); +	if ((ret == -ETIMEDOUT) || (ctx->state & GSC_CTX_ABORT)) { +		gsc_ctx_state_lock_clear(GSC_CTX_STOP_REQ | GSC_CTX_ABORT, ctx); +		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +	} +} +  static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)  {  	struct gsc_ctx *ctx = q->drv_priv; @@ -55,18 +66,13 @@ static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)  	return ret > 0 ? 0 : ret;  } -static int gsc_m2m_stop_streaming(struct vb2_queue *q) +static void gsc_m2m_stop_streaming(struct vb2_queue *q)  {  	struct gsc_ctx *ctx = q->drv_priv; -	int ret; -	ret = gsc_m2m_ctx_stop_req(ctx); -	if (ret == -ETIMEDOUT) -		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +	__gsc_m2m_job_abort(ctx);  	pm_runtime_put(&ctx->gsc_dev->pdev->dev); - -	return 0;  }  void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) @@ -80,8 +86,12 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)  	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);  	if (src_vb && dst_vb) { -		src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; -		src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; +		dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; +		dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode; +		dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +		dst_vb->v4l2_buf.flags |= +			src_vb->v4l2_buf.flags +			& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  		v4l2_m2m_buf_done(src_vb, vb_state);  		v4l2_m2m_buf_done(dst_vb, vb_state); @@ -91,15 +101,9 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)  	}  } -  static void gsc_m2m_job_abort(void *priv)  { -	struct gsc_ctx *ctx = priv; -	int ret; - -	ret = gsc_m2m_ctx_stop_req(ctx); -	if (ret == -ETIMEDOUT) -		gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); +	__gsc_m2m_job_abort((struct gsc_ctx *)priv);  }  static int gsc_get_bufs(struct gsc_ctx *ctx) @@ -150,9 +154,10 @@ static void gsc_m2m_device_run(void *priv)  		gsc->m2m.ctx = ctx;  	} -	is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0; -	ctx->state &= ~GSC_CTX_STOP_REQ; +	is_set = ctx->state & GSC_CTX_STOP_REQ;  	if (is_set) { +		ctx->state &= ~GSC_CTX_STOP_REQ; +		ctx->state |= GSC_CTX_ABORT;  		wake_up(&gsc->irq_queue);  		goto put_device;  	} @@ -587,7 +592,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->ops = &gsc_m2m_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops;  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -600,7 +605,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->ops = &gsc_m2m_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops;  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	return vb2_queue_init(dst_vq);  } diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 53ad0f08017..5dcaa0a8054 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -1,8 +1,9 @@  config VIDEO_SAMSUNG_EXYNOS4_IS  	bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver" -	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PM_RUNTIME +	depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API  	depends on (PLAT_S5P || ARCH_EXYNOS) +	depends on OF && COMMON_CLK  	help  	  Say Y here to enable camera host interface devices for  	  Samsung S5P and EXYNOS SoC series. @@ -17,7 +18,7 @@ config VIDEO_S5P_FIMC  	depends on I2C  	select VIDEOBUF2_DMA_CONTIG  	select V4L2_MEM2MEM_DEV -	select MFD_SYSCON if OF +	select MFD_SYSCON  	select VIDEO_EXYNOS4_IS_COMMON  	help  	  This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host @@ -29,7 +30,7 @@ config VIDEO_S5P_FIMC  config VIDEO_S5P_MIPI_CSIS  	tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"  	depends on REGULATOR -	select S5P_SETUP_MIPIPHY +	select GENERIC_PHY  	help  	  This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2  	  receiver (MIPI-CSIS) devices. @@ -64,4 +65,13 @@ config VIDEO_EXYNOS4_FIMC_IS  	  To compile this driver as a module, choose M here: the  	  module will be called exynos4-fimc-is. +config VIDEO_EXYNOS4_ISP_DMA_CAPTURE +	bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support" +	depends on VIDEO_EXYNOS4_FIMC_IS +	select VIDEO_EXYNOS4_IS_COMMON +	default y +	  help +	  This option enables an additional video device node exposing a V4L2 +	  video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA. +  endif # VIDEO_SAMSUNG_EXYNOS4_IS diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile index c2ff29ba685..eed1b185d81 100644 --- a/drivers/media/platform/exynos4-is/Makefile +++ b/drivers/media/platform/exynos4-is/Makefile @@ -6,6 +6,10 @@ exynos4-is-common-objs := common.o  exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o  exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o +ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y) +exynos-fimc-is-objs += fimc-isp-video.o +endif +  obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS)	+= s5p-csis.o  obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE)	+= exynos-fimc-lite.o  obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS)	+= exynos-fimc-is.o diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index 0ec210b4da1..0eb34ecb8ee 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -10,7 +10,7 @@   */  #include <linux/module.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "common.h"  /* Called with the media graph mutex held or entity->stream_count > 0. */ diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index fb27ff7e1e0..3d2babd5067 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -294,15 +294,15 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)  	return 0;  } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q)  {  	struct fimc_ctx *ctx = q->drv_priv;  	struct fimc_dev *fimc = ctx->fimc_dev;  	if (!fimc_capture_active(fimc)) -		return -EINVAL; +		return; -	return fimc_stop_capture(fimc, false); +	fimc_stop_capture(fimc, false);  }  int fimc_capture_suspend(struct fimc_dev *fimc) @@ -549,7 +549,7 @@ static int fimc_capture_release(struct file *file)  		vc->streaming = false;  	} -	ret = vb2_fop_release(file); +	ret = _vb2_fop_release(file, NULL);  	if (close) {  		clear_bit(ST_CAPT_BUSY, &fimc->state); @@ -1782,7 +1782,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,  	q->ops = &fimc_capture_qops;  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct fimc_vid_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	q->lock = &fimc->lock;  	ret = vb2_queue_init(q); diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 3d66d88ea3a..b70fd996d79 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -56,8 +56,8 @@ static struct fimc_fmt fimc_formats[] = {  		.colplanes	= 1,  		.flags		= FMT_FLAGS_M2M,  	}, { -		.name		= "ARGB8888, 32 bpp", -		.fourcc		= V4L2_PIX_FMT_RGB32, +		.name		= "BGRA8888, 32 bpp", +		.fourcc		= V4L2_PIX_FMT_BGR32,  		.depth		= { 32 },  		.color		= FIMC_FMT_RGB888,  		.memplanes	= 1, @@ -122,7 +122,7 @@ static struct fimc_fmt fimc_formats[] = {  	}, {  		.name		= "YUV 4:2:2 planar, Y/Cb/Cr",  		.fourcc		= V4L2_PIX_FMT_YUV422P, -		.depth		= { 12 }, +		.depth		= { 16 },  		.color		= FIMC_FMT_YCBYCR422,  		.memplanes	= 1,  		.colplanes	= 3, @@ -450,7 +450,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)  	bool pix_hoff = ctx->fimc_dev->drv_data->dma_pix_hoff;  	u32 i, depth = 0; -	for (i = 0; i < f->fmt->colplanes; i++) +	for (i = 0; i < f->fmt->memplanes; i++)  		depth += f->fmt->depth[i];  	f->dma_offset.y_h = f->offs_h; @@ -998,48 +998,53 @@ static int fimc_probe(struct platform_device *pdev)  	ret = devm_request_irq(dev, res->start, fimc_irq_handler,  			       0, dev_name(dev), fimc); -	if (ret) { +	if (ret < 0) {  		dev_err(dev, "failed to install irq (%d)\n", ret); -		goto err_clk; +		goto err_sclk;  	}  	ret = fimc_initialize_capture_subdev(fimc); -	if (ret) -		goto err_clk; +	if (ret < 0) +		goto err_sclk;  	platform_set_drvdata(pdev, fimc);  	pm_runtime_enable(dev); -	ret = pm_runtime_get_sync(dev); -	if (ret < 0) -		goto err_sd; + +	if (!pm_runtime_enabled(dev)) { +		ret = clk_enable(fimc->clock[CLK_GATE]); +		if (ret < 0) +			goto err_sd; +	} +  	/* Initialize contiguous memory allocator */  	fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);  	if (IS_ERR(fimc->alloc_ctx)) {  		ret = PTR_ERR(fimc->alloc_ctx); -		goto err_pm; +		goto err_gclk;  	}  	dev_dbg(dev, "FIMC.%d registered successfully\n", fimc->id); - -	pm_runtime_put(dev);  	return 0; -err_pm: -	pm_runtime_put(dev); + +err_gclk: +	if (!pm_runtime_enabled(dev)) +		clk_disable(fimc->clock[CLK_GATE]);  err_sd:  	fimc_unregister_capture_subdev(fimc); -err_clk: +err_sclk:  	clk_disable(fimc->clock[CLK_BUS]);  	fimc_clk_put(fimc);  	return ret;  } +#ifdef CONFIG_PM_RUNTIME  static int fimc_runtime_resume(struct device *dev)  {  	struct fimc_dev *fimc =	dev_get_drvdata(dev);  	dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state); -	/* Enable clocks and perform basic initalization */ +	/* Enable clocks and perform basic initialization */  	clk_enable(fimc->clock[CLK_GATE]);  	fimc_hw_reset(fimc); @@ -1065,6 +1070,7 @@ static int fimc_runtime_suspend(struct device *dev)  	dbg("fimc%d: state: 0x%lx", fimc->id, fimc->state);  	return ret;  } +#endif  #ifdef CONFIG_PM_SLEEP  static int fimc_resume(struct device *dev) diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 3d376faec77..6c75c6ced1f 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -27,7 +27,7 @@  #include <media/v4l2-device.h>  #include <media/v4l2-mem2mem.h>  #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #define dbg(fmt, args...) \  	pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args) @@ -481,7 +481,6 @@ struct fimc_ctrls {   * @flags:		additional flags for image conversion   * @state:		flags to keep track of user configuration   * @fimc_dev:		the FIMC device this context applies to - * @m2m_ctx:		memory-to-memory device context   * @fh:			v4l2 file handle   * @ctrls:		v4l2 controls structure   */ @@ -502,7 +501,6 @@ struct fimc_ctx {  	u32			flags;  	u32			state;  	struct fimc_dev		*fimc_dev; -	struct v4l2_m2m_ctx	*m2m_ctx;  	struct v4l2_fh		fh;  	struct fimc_ctrls	ctrls;  }; diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index 9bf3ddd9e02..bf1465d1bf6 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)  	__hw_param_copy(dst, src);  } -static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) +int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)  {  	struct is_param_region *par = &is->is_p_region->parameter;  	struct chain_config *cfg = &is->config[is->config_index]; diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h index f9358c27ae2..8e31f764277 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.h +++ b/drivers/media/platform/exynos4-is/fimc-is-param.h @@ -911,6 +911,10 @@ struct is_region {  	u32 shared[MAX_SHARED_COUNT];  } __packed; +/* Offset to the ISP DMA2 output buffer address array. */ +#define DMA2_OUTPUT_ADDR_ARRAY_OFFS \ +	(offsetof(struct is_region, shared) + 32 * sizeof(u32)) +  struct is_debug_frame_descriptor {  	u32 sensor_frame_time;  	u32 sensor_exposure_time; @@ -988,6 +992,7 @@ struct sensor_open_extended {  struct fimc_is;  int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); +int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset);  void fimc_is_set_initial_params(struct fimc_is *is);  unsigned int __get_pending_param_count(struct fimc_is *is); diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index f758e2694fa..cfe4406a83f 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -33,47 +33,23 @@ void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is)  	mcuctl_write(INTGR0_INTGD(0), is, MCUCTL_REG_INTGR0);  } -int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is) -{ -	unsigned int timeout = 2000; -	u32 cfg, status; - -	cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); -	status = INTSR0_GET_INTSD(0, cfg); - -	while (status) { -		cfg = mcuctl_read(is, MCUCTL_REG_INTSR0); -		status = INTSR0_GET_INTSD(0, cfg); -		if (timeout == 0) { -			dev_warn(&is->pdev->dev, "%s timeout\n", -				 __func__); -			return -ETIME; -		} -		timeout--; -		udelay(1); -	} -	return 0; -} -  int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is)  {  	unsigned int timeout = 2000;  	u32 cfg, status; -	cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0); -	status = INTMSR0_GET_INTMSD(0, cfg); - -	while (status) { +	do {  		cfg = mcuctl_read(is, MCUCTL_REG_INTMSR0);  		status = INTMSR0_GET_INTMSD(0, cfg); -		if (timeout == 0) { + +		if (--timeout == 0) {  			dev_warn(&is->pdev->dev, "%s timeout\n",  				 __func__); -			return -ETIME; +			return -ETIMEDOUT;  		} -		timeout--;  		udelay(1); -	} +	} while (status != 0); +  	return 0;  } @@ -129,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args)  	return 0;  } +void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask) +{ +	if (hweight32(mask) == 1) { +		dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n", +							__func__, mask); +		return; +	} + +	if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0) +		dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n"); + +	mcuctl_write(mask, is, MCUCTL_REG_ISSR(23)); +} +  void fimc_is_hw_set_sensor_num(struct fimc_is *is)  {  	pr_debug("setting sensor index to: %d\n", is->sensor_index); @@ -136,7 +126,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is)  	mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0));  	mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));  	mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); -	mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3)); +	mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3));  }  void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h index 5fa2fda4674..141e5ddadbe 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.h +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h @@ -145,9 +145,9 @@ void fimc_is_fw_clear_irq2(struct fimc_is *is);  int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num);  void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); -int fimc_is_hw_wait_intsr0_intsd0(struct fimc_is *is);  int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is);  void fimc_is_hw_set_sensor_num(struct fimc_is *is); +void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask);  void fimc_is_hw_stream_on(struct fimc_is *is);  void fimc_is_hw_stream_off(struct fimc_is *is);  int fimc_is_hw_set_param(struct fimc_is *is); diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c index 6647421e5d3..10e82e21b5d 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c @@ -2,276 +2,21 @@   * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver   *   * Copyright (C) 2013 Samsung Electronics Co., Ltd. - *   * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>   *   * 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.   */ -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of_gpio.h> -#include <linux/pm_runtime.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <media/v4l2-subdev.h> -#include "fimc-is.h"  #include "fimc-is-sensor.h" -#define DRIVER_NAME "FIMC-IS-SENSOR" - -static const char * const sensor_supply_names[] = { -	"svdda", -	"svddio", -}; - -static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { -	{ -		.code = V4L2_MBUS_FMT_SGRBG10_1X10, -		.colorspace = V4L2_COLORSPACE_SRGB, -		.field = V4L2_FIELD_NONE, -	} -}; - -static const struct v4l2_mbus_framefmt *find_sensor_format( -	struct v4l2_mbus_framefmt *mf) -{ -	int i; - -	for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) -		if (mf->code == fimc_is_sensor_formats[i].code) -			return &fimc_is_sensor_formats[i]; - -	return &fimc_is_sensor_formats[0]; -} - -static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, -				  struct v4l2_subdev_fh *fh, -				  struct v4l2_subdev_mbus_code_enum *code) -{ -	if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) -		return -EINVAL; - -	code->code = fimc_is_sensor_formats[code->index].code; -	return 0; -} - -static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, -				      struct v4l2_mbus_framefmt *mf) -{ -	const struct sensor_drv_data *dd = sensor->drvdata; -	const struct v4l2_mbus_framefmt *fmt; - -	fmt = find_sensor_format(mf); -	mf->code = fmt->code; -	v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, -			      &mf->height, 12 + 8, dd->height, 0, 0); -} - -static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( -		struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, -		u32 pad, enum v4l2_subdev_format_whence which) -{ -	if (which == V4L2_SUBDEV_FORMAT_TRY) -		return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; - -	return &sensor->format; -} - -static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, -				  struct v4l2_subdev_fh *fh, -				  struct v4l2_subdev_format *fmt) -{ -	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); -	struct v4l2_mbus_framefmt *mf; - -	fimc_is_sensor_try_format(sensor, &fmt->format); - -	mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); -	if (mf) { -		mutex_lock(&sensor->lock); -		if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) -			*mf = fmt->format; -		mutex_unlock(&sensor->lock); -	} -	return 0; -} - -static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, -				  struct v4l2_subdev_fh *fh, -				  struct v4l2_subdev_format *fmt) -{ -	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); -	struct v4l2_mbus_framefmt *mf; - -	mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); - -	mutex_lock(&sensor->lock); -	fmt->format = *mf; -	mutex_unlock(&sensor->lock); -	return 0; -} - -static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { -	.enum_mbus_code	= fimc_is_sensor_enum_mbus_code, -	.get_fmt	= fimc_is_sensor_get_fmt, -	.set_fmt	= fimc_is_sensor_set_fmt, -}; - -static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ -	struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); - -	*format		= fimc_is_sensor_formats[0]; -	format->width	= FIMC_IS_SENSOR_DEF_PIX_WIDTH; -	format->height	= FIMC_IS_SENSOR_DEF_PIX_HEIGHT; - -	return 0; -} - -static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { -	.open = fimc_is_sensor_open, -}; - -static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) -{ -	struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); -	int gpio = sensor->gpio_reset; -	int ret; - -	if (on) { -		ret = pm_runtime_get(sensor->dev); -		if (ret < 0) -			return ret; - -		ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, -					    sensor->supplies); -		if (ret < 0) { -			pm_runtime_put(sensor->dev); -			return ret; -		} -		if (gpio_is_valid(gpio)) { -			gpio_set_value(gpio, 1); -			usleep_range(600, 800); -			gpio_set_value(gpio, 0); -			usleep_range(10000, 11000); -			gpio_set_value(gpio, 1); -		} - -		/* A delay needed for the sensor initialization. */ -		msleep(20); -	} else { -		if (gpio_is_valid(gpio)) -			gpio_set_value(gpio, 0); - -		ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, -					     sensor->supplies); -		if (!ret) -			pm_runtime_put(sensor->dev); -	} - -	pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); - -	return ret; -} - -static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { -	.s_power = fimc_is_sensor_s_power, -}; - -static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { -	.core = &fimc_is_sensor_core_ops, -	.pad = &fimc_is_sensor_pad_ops, -}; - -static const struct of_device_id fimc_is_sensor_of_match[]; - -static int fimc_is_sensor_probe(struct i2c_client *client, -				const struct i2c_device_id *id) -{ -	struct device *dev = &client->dev; -	struct fimc_is_sensor *sensor; -	const struct of_device_id *of_id; -	struct v4l2_subdev *sd; -	int gpio, i, ret; - -	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); -	if (!sensor) -		return -ENOMEM; - -	mutex_init(&sensor->lock); -	sensor->gpio_reset = -EINVAL; - -	gpio = of_get_gpio_flags(dev->of_node, 0, NULL); -	if (gpio_is_valid(gpio)) { -		ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, -							DRIVER_NAME); -		if (ret < 0) -			return ret; -	} -	sensor->gpio_reset = gpio; - -	for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) -		sensor->supplies[i].supply = sensor_supply_names[i]; - -	ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, -				      sensor->supplies); -	if (ret < 0) -		return ret; - -	of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); -	if (!of_id) -		return -ENODEV; - -	sensor->drvdata = of_id->data; -	sensor->dev = dev; - -	sd = &sensor->subdev; -	v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); -	snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); -	sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - -	sensor->format.code = fimc_is_sensor_formats[0].code; -	sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; -	sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; - -	sensor->pad.flags = MEDIA_PAD_FL_SOURCE; -	ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); -	if (ret < 0) -		return ret; - -	pm_runtime_no_callbacks(dev); -	pm_runtime_enable(dev); - -	return ret; -} - -static int fimc_is_sensor_remove(struct i2c_client *client) -{ -	struct v4l2_subdev *sd = i2c_get_clientdata(client); -	media_entity_cleanup(&sd->entity); -	return 0; -} - -static const struct i2c_device_id fimc_is_sensor_ids[] = { -	{ } -}; -  static const struct sensor_drv_data s5k6a3_drvdata = {  	.id		= FIMC_IS_SENSOR_ID_S5K6A3, -	.subdev_name	= "S5K6A3", -	.width		= S5K6A3_SENSOR_WIDTH, -	.height		= S5K6A3_SENSOR_HEIGHT, +	.open_timeout	= S5K6A3_OPEN_TIMEOUT,  }; -static const struct of_device_id fimc_is_sensor_of_match[] = { +static const struct of_device_id fimc_is_sensor_of_ids[] = {  	{  		.compatible	= "samsung,s5k6a3",  		.data		= &s5k6a3_drvdata, @@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = {  	{  }  }; -static struct i2c_driver fimc_is_sensor_driver = { -	.driver = { -		.of_match_table	= fimc_is_sensor_of_match, -		.name		= DRIVER_NAME, -		.owner		= THIS_MODULE, -	}, -	.probe		= fimc_is_sensor_probe, -	.remove		= fimc_is_sensor_remove, -	.id_table	= fimc_is_sensor_ids, -}; - -int fimc_is_register_sensor_driver(void) +const struct sensor_drv_data *fimc_is_sensor_get_drvdata( +			struct device_node *node)  { -	return i2c_add_driver(&fimc_is_sensor_driver); -} +	const struct of_device_id *of_id; -void fimc_is_unregister_sensor_driver(void) -{ -	i2c_del_driver(&fimc_is_sensor_driver); +	of_id = of_match_node(fimc_is_sensor_of_ids, node); +	return of_id ? of_id->data : NULL;  } - -MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h index 6036d49a6c6..173ccffa4bc 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h @@ -13,24 +13,13 @@  #ifndef FIMC_IS_SENSOR_H_  #define FIMC_IS_SENSOR_H_ -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/regulator/consumer.h> -#include <linux/videodev2.h> -#include <media/v4l2-subdev.h> - -#define FIMC_IS_SENSOR_OPEN_TIMEOUT	2000 /* ms */ - -#define FIMC_IS_SENSOR_DEF_PIX_WIDTH	1296 -#define FIMC_IS_SENSOR_DEF_PIX_HEIGHT	732 +#include <linux/of.h> +#include <linux/types.h> +#define S5K6A3_OPEN_TIMEOUT		2000 /* ms */  #define S5K6A3_SENSOR_WIDTH		1392  #define S5K6A3_SENSOR_HEIGHT		1392 -#define SENSOR_NUM_SUPPLIES		2 -  enum fimc_is_sensor_id {  	FIMC_IS_SENSOR_ID_S5K3H2 = 1,  	FIMC_IS_SENSOR_ID_S5K6A3, @@ -45,45 +34,23 @@ enum fimc_is_sensor_id {  struct sensor_drv_data {  	enum fimc_is_sensor_id id; -	const char * const subdev_name; -	unsigned int width; -	unsigned int height; +	/* sensor open timeout in ms */ +	unsigned short open_timeout;  };  /**   * struct fimc_is_sensor - fimc-is sensor data structure - * @dev: pointer to this I2C client device structure - * @subdev: the image sensor's v4l2 subdev - * @pad: subdev media source pad - * @supplies: image sensor's voltage regulator supplies - * @gpio_reset: GPIO connected to the sensor's reset pin   * @drvdata: a pointer to the sensor's parameters data structure   * @i2c_bus: ISP I2C bus index (0...1)   * @test_pattern: true to enable video test pattern - * @lock: mutex protecting the structure's members below - * @format: media bus format at the sensor's source pad   */  struct fimc_is_sensor { -	struct device *dev; -	struct v4l2_subdev subdev; -	struct media_pad pad; -	struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES]; -	int gpio_reset;  	const struct sensor_drv_data *drvdata;  	unsigned int i2c_bus; -	bool test_pattern; - -	struct mutex lock; -	struct v4l2_mbus_framefmt format; +	u8 test_pattern;  }; -static inline -struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) -{ -	return container_of(sd, struct fimc_is_sensor, subdev); -} - -int fimc_is_register_sensor_driver(void); -void fimc_is_unregister_sensor_driver(void); +const struct sensor_drv_data *fimc_is_sensor_get_drvdata( +				struct device_node *node);  #endif /* FIMC_IS_SENSOR_H_ */ diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 9770fa98d6a..5476dce3ad2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -24,13 +24,13 @@  #include <linux/i2c.h>  #include <linux/of_irq.h>  #include <linux/of_address.h> +#include <linux/of_graph.h>  #include <linux/of_platform.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h>  #include <linux/slab.h>  #include <linux/types.h>  #include <linux/videodev2.h> -#include <media/v4l2-of.h>  #include <media/videobuf2-dma-contig.h>  #include "media-dev.h" @@ -161,78 +161,69 @@ static void fimc_is_disable_clocks(struct fimc_is *is)  	}  } -static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, -				       struct device_node *np) +static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, +						struct device_node *node)  { +	struct fimc_is_sensor *sensor = &is->sensor[index];  	u32 tmp = 0;  	int ret; -	np = v4l2_of_get_next_endpoint(np, NULL); -	if (!np) +	sensor->drvdata = fimc_is_sensor_get_drvdata(node); +	if (!sensor->drvdata) { +		dev_err(&is->pdev->dev, "no driver data found for: %s\n", +							 node->full_name); +		return -EINVAL; +	} + +	node = of_graph_get_next_endpoint(node, NULL); +	if (!node)  		return -ENXIO; -	np = v4l2_of_get_remote_port(np); -	if (!np) + +	node = of_graph_get_remote_port(node); +	if (!node)  		return -ENXIO;  	/* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ -	ret = of_property_read_u32(np, "reg", &tmp); -	sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; +	ret = of_property_read_u32(node, "reg", &tmp); +	if (ret < 0) { +		dev_err(&is->pdev->dev, "reg property not found at: %s\n", +							 node->full_name); +		return ret; +	} -	return ret; +	sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; +	return 0;  }  static int fimc_is_register_subdevs(struct fimc_is *is)  { -	struct device_node *adapter, *child; -	int ret; +	struct device_node *i2c_bus, *child; +	int ret, index = 0;  	ret = fimc_isp_subdev_create(&is->isp);  	if (ret < 0)  		return ret; -	for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { -		if (!of_find_device_by_node(adapter)) { -			of_node_put(adapter); -			return -EPROBE_DEFER; -		} - -		for_each_available_child_of_node(adapter, child) { -			struct i2c_client *client; -			struct v4l2_subdev *sd; - -			client = of_find_i2c_device_by_node(child); -			if (!client) -				goto e_retry; - -			sd = i2c_get_clientdata(client); -			if (!sd) -				goto e_retry; +	/* Initialize memory allocator context for the ISP DMA. */ +	is->isp.alloc_ctx = is->alloc_ctx; -			/* FIXME: Add support for multiple sensors. */ -			if (WARN_ON(is->sensor)) -				continue; +	for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { +		for_each_available_child_of_node(i2c_bus, child) { +			ret = fimc_is_parse_sensor_config(is, index, child); -			is->sensor = sd_to_fimc_is_sensor(sd); - -			if (fimc_is_parse_sensor_config(is->sensor, child)) { -				dev_warn(&is->pdev->dev, "DT parse error: %s\n", -							 child->full_name); +			if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { +				of_node_put(child); +				return ret;  			} -			pr_debug("%s(): registered subdev: %p\n", -				 __func__, sd->name); +			index++;  		}  	}  	return 0; - -e_retry: -	of_node_put(child); -	return -EPROBE_DEFER;  }  static int fimc_is_unregister_subdevs(struct fimc_is *is)  {  	fimc_isp_subdev_destroy(&is->isp); -	is->sensor = NULL;  	return 0;  } @@ -376,6 +367,9 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is)  {  	struct device *dev = &is->pdev->dev; +	if (is->memory.vaddr == NULL) +		return; +  	dma_free_coherent(dev, is->memory.size, is->memory.vaddr,  			  is->memory.paddr);  } @@ -647,7 +641,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is,  	fimc_is_hw_set_intgr0_gd0(is);  	return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, -				  FIMC_IS_SENSOR_OPEN_TIMEOUT); +				  sensor->drvdata->open_timeout);  } @@ -661,8 +655,8 @@ int fimc_is_hw_initialize(struct fimc_is *is)  	u32 prev_id;  	int i, ret; -	/* Sensor initialization. */ -	ret = fimc_is_hw_open_sensor(is, is->sensor); +	/* Sensor initialization. Only one sensor is currently supported. */ +	ret = fimc_is_hw_open_sensor(is, &is->sensor[0]);  	if (ret < 0)  		return ret; @@ -781,6 +775,9 @@ static int fimc_is_debugfs_create(struct fimc_is *is)  	return is->debugfs_entry == NULL ? -EIO : 0;  } +static int fimc_is_runtime_resume(struct device *dev); +static int fimc_is_runtime_suspend(struct device *dev); +  static int fimc_is_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -835,14 +832,20 @@ static int fimc_is_probe(struct platform_device *pdev)  	}  	pm_runtime_enable(dev); +	if (!pm_runtime_enabled(dev)) { +		ret = fimc_is_runtime_resume(dev); +		if (ret < 0) +			goto err_irq; +	} +  	ret = pm_runtime_get_sync(dev);  	if (ret < 0) -		goto err_irq; +		goto err_pm;  	is->alloc_ctx = vb2_dma_contig_init_ctx(dev);  	if (IS_ERR(is->alloc_ctx)) {  		ret = PTR_ERR(is->alloc_ctx); -		goto err_irq; +		goto err_pm;  	}  	/*  	 * Register FIMC-IS V4L2 subdevs to this driver. The video nodes @@ -867,10 +870,13 @@ static int fimc_is_probe(struct platform_device *pdev)  err_dfs:  	fimc_is_debugfs_remove(is); -err_vb: -	vb2_dma_contig_cleanup_ctx(is->alloc_ctx);  err_sd:  	fimc_is_unregister_subdevs(is); +err_vb: +	vb2_dma_contig_cleanup_ctx(is->alloc_ctx); +err_pm: +	if (!pm_runtime_enabled(dev)) +		fimc_is_runtime_suspend(dev);  err_irq:  	free_irq(is->irq, is);  err_clk: @@ -919,10 +925,13 @@ static int fimc_is_suspend(struct device *dev)  static int fimc_is_remove(struct platform_device *pdev)  { -	struct fimc_is *is = platform_get_drvdata(pdev); +	struct device *dev = &pdev->dev; +	struct fimc_is *is = dev_get_drvdata(dev); -	pm_runtime_disable(&pdev->dev); -	pm_runtime_set_suspended(&pdev->dev); +	pm_runtime_disable(dev); +	pm_runtime_set_suspended(dev); +	if (!pm_runtime_status_suspended(dev)) +		fimc_is_runtime_suspend(dev);  	free_irq(is->irq, is);  	fimc_is_unregister_subdevs(is);  	vb2_dma_contig_cleanup_ctx(is->alloc_ctx); @@ -962,27 +971,20 @@ static int fimc_is_module_init(void)  {  	int ret; -	ret = fimc_is_register_sensor_driver(); -	if (ret < 0) -		return ret; -  	ret = fimc_is_register_i2c_driver();  	if (ret < 0) -		goto err_sens; +		return ret;  	ret = platform_driver_register(&fimc_is_driver); -	if (!ret) -		return ret; -	fimc_is_unregister_i2c_driver(); -err_sens: -	fimc_is_unregister_sensor_driver(); +	if (ret < 0) +		fimc_is_unregister_i2c_driver(); +  	return ret;  }  static void fimc_is_module_exit(void)  { -	fimc_is_unregister_sensor_driver();  	fimc_is_unregister_i2c_driver();  	platform_driver_unregister(&fimc_is_driver);  } diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 61bb0127e19..e0be691af2d 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -39,7 +39,7 @@  #define FIMC_IS_FW_LOAD_TIMEOUT		1000 /* ms */  #define FIMC_IS_POWER_ON_TIMEOUT	1000 /* us */ -#define FIMC_IS_SENSOR_NUM		2 +#define FIMC_IS_SENSORS_NUM		2  /* Memory definitions */  #define FIMC_IS_CPU_MEM_SIZE		(0xa00000) @@ -253,7 +253,7 @@ struct fimc_is {  	struct firmware			*f_w;  	struct fimc_isp			isp; -	struct fimc_is_sensor		*sensor; +	struct fimc_is_sensor		sensor[FIMC_IS_SENSORS_NUM];  	struct fimc_is_setfile		setfile;  	struct vb2_alloc_ctx		*alloc_ctx; @@ -292,6 +292,11 @@ static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp)  	return container_of(isp, struct fimc_is, isp);  } +static inline struct chain_config *__get_curr_is_config(struct fimc_is *is) +{ +	return &is->config[is->config_index]; +} +  static inline void fimc_is_mem_barrier(void)  {  	mb(); diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c new file mode 100644 index 00000000000..93f9cf2ebcd --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -0,0 +1,659 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * FIMC-IS ISP video input and video output DMA interface driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * The hardware handling code derived from a driver written by + * Younghwan Joo <yhwan.joo@samsung.com>. + * + * 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. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/printk.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> +#include <media/exynos-fimc.h> + +#include "common.h" +#include "media-dev.h" +#include "fimc-is.h" +#include "fimc-isp-video.h" +#include "fimc-is-param.h" + +static int isp_video_capture_queue_setup(struct vb2_queue *vq, +			const struct v4l2_format *pfmt, +			unsigned int *num_buffers, unsigned int *num_planes, +			unsigned int sizes[], void *allocators[]) +{ +	struct fimc_isp *isp = vb2_get_drv_priv(vq); +	struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; +	const struct v4l2_pix_format_mplane *pixm = NULL; +	const struct fimc_fmt *fmt; +	unsigned int wh, i; + +	if (pfmt) { +		pixm = &pfmt->fmt.pix_mp; +		fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1); +		wh = pixm->width * pixm->height; +	} else { +		fmt = isp->video_capture.format; +		wh = vid_fmt->width * vid_fmt->height; +	} + +	if (fmt == NULL) +		return -EINVAL; + +	*num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN, +						FIMC_ISP_REQ_BUFS_MAX); +	*num_planes = fmt->memplanes; + +	for (i = 0; i < fmt->memplanes; i++) { +		unsigned int size = (wh * fmt->depth[i]) / 8; +		if (pixm) +			sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); +		else +			sizes[i] = size; +		allocators[i] = isp->alloc_ctx; +	} + +	return 0; +} + +static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is) +{ +	return &__get_curr_is_config(is)->isp.dma2_output; +} + +static int isp_video_capture_start_streaming(struct vb2_queue *q, +						unsigned int count) +{ +	struct fimc_isp *isp = vb2_get_drv_priv(q); +	struct fimc_is *is = fimc_isp_to_is(isp); +	struct param_dma_output *dma = __get_isp_dma2(is); +	struct fimc_is_video *video = &isp->video_capture; +	int ret; + +	if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) || +	    test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) +		return 0; + + +	dma->cmd = DMA_OUTPUT_COMMAND_ENABLE; +	dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE; +	dma->buffer_address = is->is_dma_p_region + +				DMA2_OUTPUT_ADDR_ARRAY_OFFS; +	dma->buffer_number = video->reqbufs_count; +	dma->dma_out_mask = video->buf_mask; + +	isp_dbg(2, &video->ve.vdev, +		"buf_count: %d, planes: %d, dma addr table: %#x\n", +		video->buf_count, video->format->memplanes, +		dma->buffer_address); + +	fimc_is_mem_barrier(); + +	fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); +	__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); + +	ret = fimc_is_itf_s_param(is, false); +	if (ret < 0) +		return ret; + +	ret = fimc_pipeline_call(&video->ve, set_stream, 1); +	if (ret < 0) +		return ret; + +	set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); +	return ret; +} + +static void isp_video_capture_stop_streaming(struct vb2_queue *q) +{ +	struct fimc_isp *isp = vb2_get_drv_priv(q); +	struct fimc_is *is = fimc_isp_to_is(isp); +	struct param_dma_output *dma = __get_isp_dma2(is); +	int ret; + +	ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); +	if (ret < 0) +		return; + +	dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; +	dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; +	dma->buffer_number = 0; +	dma->buffer_address = 0; +	dma->dma_out_mask = 0; + +	fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); +	__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); + +	ret = fimc_is_itf_s_param(is, false); +	if (ret < 0) +		dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__); + +	fimc_is_hw_set_isp_buf_mask(is, 0); + +	clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); +	clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); + +	isp->video_capture.buf_count = 0; +} + +static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) +{ +	struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); +	struct fimc_is_video *video = &isp->video_capture; +	int i; + +	if (video->format == NULL) +		return -EINVAL; + +	for (i = 0; i < video->format->memplanes; i++) { +		unsigned long size = video->pixfmt.plane_fmt[i].sizeimage; + +		if (vb2_plane_size(vb, i) < size) { +			v4l2_err(&video->ve.vdev, +				 "User buffer too small (%ld < %ld)\n", +				 vb2_plane_size(vb, i), size); +			return -EINVAL; +		} +		vb2_set_plane_payload(vb, i, size); +	} + +	/* Check if we get one of the already known buffers. */ +	if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { +		dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); +		int i; + +		for (i = 0; i < video->buf_count; i++) +			if (video->buffers[i]->dma_addr[0] == dma_addr) +				return 0; +		return -ENXIO; +	} + +	return 0; +} + +static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) +{ +	struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); +	struct fimc_is_video *video = &isp->video_capture; +	struct fimc_is *is = fimc_isp_to_is(isp); +	struct isp_video_buf *ivb = to_isp_video_buf(vb); +	unsigned long flags; +	unsigned int i; + +	if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { +		spin_lock_irqsave(&is->slock, flags); +		video->buf_mask |= BIT(ivb->index); +		spin_unlock_irqrestore(&is->slock, flags); +	} else { +		unsigned int num_planes = video->format->memplanes; + +		ivb->index = video->buf_count; +		video->buffers[ivb->index] = ivb; + +		for (i = 0; i < num_planes; i++) { +			int buf_index = ivb->index * num_planes + i; + +			ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); +			is->is_p_region->shared[32 + buf_index] = +							ivb->dma_addr[i]; + +			isp_dbg(2, &video->ve.vdev, +				"dma_buf %d (%d/%d/%d) addr: %#x\n", +				buf_index, ivb->index, i, vb->v4l2_buf.index, +				ivb->dma_addr[i]); +		} + +		if (++video->buf_count < video->reqbufs_count) +			return; + +		video->buf_mask = (1UL << video->buf_count) - 1; +		set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); +	} + +	if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) +		isp_video_capture_start_streaming(vb->vb2_queue, 0); +} + +/* + * FIMC-IS ISP input and output DMA interface interrupt handler. + * Locking: called with is->slock spinlock held. + */ +void fimc_isp_video_irq_handler(struct fimc_is *is) +{ +	struct fimc_is_video *video = &is->isp.video_capture; +	struct vb2_buffer *vb; +	int buf_index; + +	/* TODO: Ensure the DMA is really stopped in stop_streaming callback */ +	if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state)) +		return; + +	buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count; +	vb = &video->buffers[buf_index]->vb; + +	v4l2_get_timestamp(&vb->v4l2_buf.timestamp); +	vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + +	video->buf_mask &= ~BIT(buf_index); +	fimc_is_hw_set_isp_buf_mask(is, video->buf_mask); +} + +static const struct vb2_ops isp_video_capture_qops = { +	.queue_setup	 = isp_video_capture_queue_setup, +	.buf_prepare	 = isp_video_capture_buffer_prepare, +	.buf_queue	 = isp_video_capture_buffer_queue, +	.wait_prepare	 = vb2_ops_wait_prepare, +	.wait_finish	 = vb2_ops_wait_finish, +	.start_streaming = isp_video_capture_start_streaming, +	.stop_streaming	 = isp_video_capture_stop_streaming, +}; + +static int isp_video_open(struct file *file) +{ +	struct fimc_isp *isp = video_drvdata(file); +	struct exynos_video_entity *ve = &isp->video_capture.ve; +	struct media_entity *me = &ve->vdev.entity; +	int ret; + +	if (mutex_lock_interruptible(&isp->video_lock)) +		return -ERESTARTSYS; + +	ret = v4l2_fh_open(file); +	if (ret < 0) +		goto unlock; + +	ret = pm_runtime_get_sync(&isp->pdev->dev); +	if (ret < 0) +		goto rel_fh; + +	if (v4l2_fh_is_singular_file(file)) { +		mutex_lock(&me->parent->graph_mutex); + +		ret = fimc_pipeline_call(ve, open, me, true); + +		/* Mark the video pipeline as in use. */ +		if (ret == 0) +			me->use_count++; + +		mutex_unlock(&me->parent->graph_mutex); +	} +	if (!ret) +		goto unlock; +rel_fh: +	v4l2_fh_release(file); +unlock: +	mutex_unlock(&isp->video_lock); +	return ret; +} + +static int isp_video_release(struct file *file) +{ +	struct fimc_isp *isp = video_drvdata(file); +	struct fimc_is_video *ivc = &isp->video_capture; +	struct media_entity *entity = &ivc->ve.vdev.entity; +	struct media_device *mdev = entity->parent; +	int ret = 0; + +	mutex_lock(&isp->video_lock); + +	if (v4l2_fh_is_singular_file(file) && ivc->streaming) { +		media_entity_pipeline_stop(entity); +		ivc->streaming = 0; +	} + +	vb2_fop_release(file); + +	if (v4l2_fh_is_singular_file(file)) { +		fimc_pipeline_call(&ivc->ve, close); + +		mutex_lock(&mdev->graph_mutex); +		entity->use_count--; +		mutex_unlock(&mdev->graph_mutex); +	} + +	pm_runtime_put(&isp->pdev->dev); +	mutex_unlock(&isp->video_lock); + +	return ret; +} + +static const struct v4l2_file_operations isp_video_fops = { +	.owner		= THIS_MODULE, +	.open		= isp_video_open, +	.release	= isp_video_release, +	.poll		= vb2_fop_poll, +	.unlocked_ioctl	= video_ioctl2, +	.mmap		= vb2_fop_mmap, +}; + +/* + * Video node ioctl operations + */ +static int isp_video_querycap(struct file *file, void *priv, +					struct v4l2_capability *cap) +{ +	struct fimc_isp *isp = video_drvdata(file); + +	__fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING); +	return 0; +} + +static int isp_video_enum_fmt_mplane(struct file *file, void *priv, +					struct v4l2_fmtdesc *f) +{ +	const struct fimc_fmt *fmt; + +	if (f->index >= FIMC_ISP_NUM_FORMATS) +		return -EINVAL; + +	fmt = fimc_isp_find_format(NULL, NULL, f->index); +	if (WARN_ON(fmt == NULL)) +		return -EINVAL; + +	strlcpy(f->description, fmt->name, sizeof(f->description)); +	f->pixelformat = fmt->fourcc; + +	return 0; +} + +static int isp_video_g_fmt_mplane(struct file *file, void *fh, +					struct v4l2_format *f) +{ +	struct fimc_isp *isp = video_drvdata(file); + +	f->fmt.pix_mp = isp->video_capture.pixfmt; +	return 0; +} + +static void __isp_video_try_fmt(struct fimc_isp *isp, +				struct v4l2_pix_format_mplane *pixm, +				const struct fimc_fmt **fmt) +{ +	*fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); + +	pixm->colorspace = V4L2_COLORSPACE_SRGB; +	pixm->field = V4L2_FIELD_NONE; +	pixm->num_planes = (*fmt)->memplanes; +	pixm->pixelformat = (*fmt)->fourcc; +	/* +	 * TODO: double check with the docmentation these width/height +	 * constraints are correct. +	 */ +	v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN, +			      FIMC_ISP_SOURCE_WIDTH_MAX, 3, +			      &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN, +			      FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0); +} + +static int isp_video_try_fmt_mplane(struct file *file, void *fh, +					struct v4l2_format *f) +{ +	struct fimc_isp *isp = video_drvdata(file); + +	__isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL); +	return 0; +} + +static int isp_video_s_fmt_mplane(struct file *file, void *priv, +					struct v4l2_format *f) +{ +	struct fimc_isp *isp = video_drvdata(file); +	struct fimc_is *is = fimc_isp_to_is(isp); +	struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; +	const struct fimc_fmt *ifmt = NULL; +	struct param_dma_output *dma = __get_isp_dma2(is); + +	__isp_video_try_fmt(isp, pixm, &ifmt); + +	if (WARN_ON(ifmt == NULL)) +		return -EINVAL; + +	dma->format = DMA_OUTPUT_FORMAT_BAYER; +	dma->order = DMA_OUTPUT_ORDER_GB_BG; +	dma->plane = ifmt->memplanes; +	dma->bitwidth = ifmt->depth[0]; +	dma->width = pixm->width; +	dma->height = pixm->height; + +	fimc_is_mem_barrier(); + +	isp->video_capture.format = ifmt; +	isp->video_capture.pixfmt = *pixm; + +	return 0; +} + +/* + * Check for source/sink format differences at each link. + * Return 0 if the formats match or -EPIPE otherwise. + */ +static int isp_video_pipeline_validate(struct fimc_isp *isp) +{ +	struct v4l2_subdev *sd = &isp->subdev; +	struct v4l2_subdev_format sink_fmt, src_fmt; +	struct media_pad *pad; +	int ret; + +	while (1) { +		/* Retrieve format at the sink pad */ +		pad = &sd->entity.pads[0]; +		if (!(pad->flags & MEDIA_PAD_FL_SINK)) +			break; +		sink_fmt.pad = pad->index; +		sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; +		ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); +		if (ret < 0 && ret != -ENOIOCTLCMD) +			return -EPIPE; + +		/* Retrieve format at the source pad */ +		pad = media_entity_remote_pad(pad); +		if (pad == NULL || +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) +			break; + +		sd = media_entity_to_v4l2_subdev(pad->entity); +		src_fmt.pad = pad->index; +		src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; +		ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); +		if (ret < 0 && ret != -ENOIOCTLCMD) +			return -EPIPE; + +		if (src_fmt.format.width != sink_fmt.format.width || +		    src_fmt.format.height != sink_fmt.format.height || +		    src_fmt.format.code != sink_fmt.format.code) +			return -EPIPE; +	} + +	return 0; +} + +static int isp_video_streamon(struct file *file, void *priv, +				      enum v4l2_buf_type type) +{ +	struct fimc_isp *isp = video_drvdata(file); +	struct exynos_video_entity *ve = &isp->video_capture.ve; +	struct media_entity *me = &ve->vdev.entity; +	int ret; + +	ret = media_entity_pipeline_start(me, &ve->pipe->mp); +	if (ret < 0) +		return ret; + +	ret = isp_video_pipeline_validate(isp); +	if (ret < 0) +		goto p_stop; + +	ret = vb2_ioctl_streamon(file, priv, type); +	if (ret < 0) +		goto p_stop; + +	isp->video_capture.streaming = 1; +	return 0; +p_stop: +	media_entity_pipeline_stop(me); +	return ret; +} + +static int isp_video_streamoff(struct file *file, void *priv, +					enum v4l2_buf_type type) +{ +	struct fimc_isp *isp = video_drvdata(file); +	struct fimc_is_video *video = &isp->video_capture; +	int ret; + +	ret = vb2_ioctl_streamoff(file, priv, type); +	if (ret < 0) +		return ret; + +	media_entity_pipeline_stop(&video->ve.vdev.entity); +	video->streaming = 0; +	return 0; +} + +static int isp_video_reqbufs(struct file *file, void *priv, +				struct v4l2_requestbuffers *rb) +{ +	struct fimc_isp *isp = video_drvdata(file); +	int ret; + +	ret = vb2_ioctl_reqbufs(file, priv, rb); +	if (ret < 0) +		return ret; + +	if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) { +		rb->count = 0; +		vb2_ioctl_reqbufs(file, priv, rb); +		ret = -ENOMEM; +	} + +	isp->video_capture.reqbufs_count = rb->count; +	return ret; +} + +static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { +	.vidioc_querycap		= isp_video_querycap, +	.vidioc_enum_fmt_vid_cap_mplane	= isp_video_enum_fmt_mplane, +	.vidioc_try_fmt_vid_cap_mplane	= isp_video_try_fmt_mplane, +	.vidioc_s_fmt_vid_cap_mplane	= isp_video_s_fmt_mplane, +	.vidioc_g_fmt_vid_cap_mplane	= isp_video_g_fmt_mplane, +	.vidioc_reqbufs			= isp_video_reqbufs, +	.vidioc_querybuf		= vb2_ioctl_querybuf, +	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf, +	.vidioc_create_bufs		= vb2_ioctl_create_bufs, +	.vidioc_qbuf			= vb2_ioctl_qbuf, +	.vidioc_dqbuf			= vb2_ioctl_dqbuf, +	.vidioc_streamon		= isp_video_streamon, +	.vidioc_streamoff		= isp_video_streamoff, +}; + +int fimc_isp_video_device_register(struct fimc_isp *isp, +				   struct v4l2_device *v4l2_dev, +				   enum v4l2_buf_type type) +{ +	struct vb2_queue *q = &isp->video_capture.vb_queue; +	struct fimc_is_video *iv; +	struct video_device *vdev; +	int ret; + +	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) +		iv = &isp->video_capture; +	else +		return -ENOSYS; + +	mutex_init(&isp->video_lock); +	INIT_LIST_HEAD(&iv->pending_buf_q); +	INIT_LIST_HEAD(&iv->active_buf_q); +	iv->format = fimc_isp_find_format(NULL, NULL, 0); +	iv->pixfmt.width = IS_DEFAULT_WIDTH; +	iv->pixfmt.height = IS_DEFAULT_HEIGHT; +	iv->pixfmt.pixelformat = iv->format->fourcc; +	iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB; +	iv->reqbufs_count = 0; + +	memset(q, 0, sizeof(*q)); +	q->type = type; +	q->io_modes = VB2_MMAP | VB2_USERPTR; +	q->ops = &isp_video_capture_qops; +	q->mem_ops = &vb2_dma_contig_memops; +	q->buf_struct_size = sizeof(struct isp_video_buf); +	q->drv_priv = isp; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->lock = &isp->video_lock; + +	ret = vb2_queue_init(q); +	if (ret < 0) +		return ret; + +	vdev = &iv->ve.vdev; +	memset(vdev, 0, sizeof(*vdev)); +	snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s", +			type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? +			"capture" : "output"); +	vdev->queue = q; +	vdev->fops = &isp_video_fops; +	vdev->ioctl_ops = &isp_video_ioctl_ops; +	vdev->v4l2_dev = v4l2_dev; +	vdev->minor = -1; +	vdev->release = video_device_release_empty; +	vdev->lock = &isp->video_lock; + +	iv->pad.flags = MEDIA_PAD_FL_SINK; +	ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0); +	if (ret < 0) +		return ret; + +	video_set_drvdata(vdev, isp); + +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); +	if (ret < 0) { +		media_entity_cleanup(&vdev->entity); +		return ret; +	} + +	v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", +		  vdev->name, video_device_node_name(vdev)); + +	return 0; +} + +void fimc_isp_video_device_unregister(struct fimc_isp *isp, +				      enum v4l2_buf_type type) +{ +	struct exynos_video_entity *ve; + +	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) +		ve = &isp->video_capture.ve; +	else +		return; + +	mutex_lock(&isp->video_lock); + +	if (video_is_registered(&ve->vdev)) { +		video_unregister_device(&ve->vdev); +		media_entity_cleanup(&ve->vdev.entity); +		ve->pipe = NULL; +	} + +	mutex_unlock(&isp->video_lock); +} diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h new file mode 100644 index 00000000000..98c662654bb --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.h @@ -0,0 +1,44 @@ +/* + * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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. + */ +#ifndef FIMC_ISP_VIDEO__ +#define FIMC_ISP_VIDEO__ + +#include <media/videobuf2-core.h> +#include "fimc-isp.h" + +#ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE +int fimc_isp_video_device_register(struct fimc_isp *isp, +				struct v4l2_device *v4l2_dev, +				enum v4l2_buf_type type); + +void fimc_isp_video_device_unregister(struct fimc_isp *isp, +				enum v4l2_buf_type type); + +void fimc_isp_video_irq_handler(struct fimc_is *is); +#else +static inline void fimc_isp_video_irq_handler(struct fimc_is *is) +{ +} + +static inline int fimc_isp_video_device_register(struct fimc_isp *isp, +						struct v4l2_device *v4l2_dev, +						enum v4l2_buf_type type) +{ +	return 0; +} + +void fimc_isp_video_device_unregister(struct fimc_isp *isp, +				enum v4l2_buf_type type) +{ +} +#endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */ + +#endif /* FIMC_ISP_VIDEO__ */ diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index d2e6cba3566..be62d6b9ac4 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -25,6 +25,7 @@  #include <media/v4l2-device.h>  #include "media-dev.h" +#include "fimc-isp-video.h"  #include "fimc-is-command.h"  #include "fimc-is-param.h"  #include "fimc-is-regs.h" @@ -93,8 +94,8 @@ void fimc_isp_irq_handler(struct fimc_is *is)  	is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21));  	fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); +	fimc_isp_video_irq_handler(is); -	/* TODO: Complete ISP DMA interrupt handler */  	wake_up(&is->irq_queue);  } @@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd,  	return 0;  } +static int fimc_isp_subdev_registered(struct v4l2_subdev *sd) +{ +	struct fimc_isp *isp = v4l2_get_subdevdata(sd); +	int ret; + +	/* Use pipeline object allocated by the media device. */ +	isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd); + +	ret = fimc_isp_video_device_register(isp, sd->v4l2_dev, +			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); +	if (ret < 0) +		isp->video_capture.ve.pipe = NULL; + +	return ret; +} + +static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd) +{ +	struct fimc_isp *isp = v4l2_get_subdevdata(sd); + +	fimc_isp_video_device_unregister(isp, +			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); +} +  static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { +	.registered = fimc_isp_subdev_registered, +	.unregistered = fimc_isp_subdev_unregistered,  	.open = fimc_isp_subdev_open,  }; @@ -511,7 +538,7 @@ static int __ctrl_set_metering(struct fimc_is *is, unsigned int value)  		break;  	default:  		return -EINVAL; -	}; +	}  	__is_set_isp_metering(is, IS_METERING_CONFIG_CMD, val);  	return 0; diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index 03bf95ab017..b99be09b49f 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -24,7 +24,7 @@  #include <media/videobuf2-core.h>  #include <media/v4l2-device.h>  #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  extern int fimc_isp_debug; @@ -35,17 +35,18 @@ extern int fimc_isp_debug;  #define FIMC_ISP_SINK_WIDTH_MIN		(16 + 8)  #define FIMC_ISP_SINK_HEIGHT_MIN	(12 + 8)  #define FIMC_ISP_SOURCE_WIDTH_MIN	8 -#define FIMC_ISP_SOURC_HEIGHT_MIN	8 +#define FIMC_ISP_SOURCE_HEIGHT_MIN	8  #define FIMC_ISP_CAC_MARGIN_WIDTH	16  #define FIMC_ISP_CAC_MARGIN_HEIGHT	12  #define FIMC_ISP_SINK_WIDTH_MAX		(4000 - 16)  #define FIMC_ISP_SINK_HEIGHT_MAX	(4000 + 12)  #define FIMC_ISP_SOURCE_WIDTH_MAX	4000 -#define FIMC_ISP_SOURC_HEIGHT_MAX	4000 +#define FIMC_ISP_SOURCE_HEIGHT_MAX	4000  #define FIMC_ISP_NUM_FORMATS		3  #define FIMC_ISP_REQ_BUFS_MIN		2 +#define FIMC_ISP_REQ_BUFS_MAX		32  #define FIMC_ISP_SD_PAD_SINK		0  #define FIMC_ISP_SD_PAD_SRC_FIFO	1 @@ -100,6 +101,16 @@ struct fimc_isp_ctrls {  	struct v4l2_ctrl *colorfx;  }; +struct isp_video_buf { +	struct vb2_buffer vb; +	dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES]; +	unsigned int index; +}; + +#define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb) + +#define FIMC_ISP_MAX_BUFS	4 +  /**   * struct fimc_is_video - fimc-is video device structure   * @vdev: video_device structure @@ -114,18 +125,26 @@ struct fimc_isp_ctrls {   * @format: current pixel format   */  struct fimc_is_video { -	struct video_device	vdev; +	struct exynos_video_entity ve;  	enum v4l2_buf_type	type;  	struct media_pad	pad;  	struct list_head	pending_buf_q;  	struct list_head	active_buf_q;  	struct vb2_queue	vb_queue; -	unsigned int		frame_count;  	unsigned int		reqbufs_count; +	unsigned int		buf_count; +	unsigned int		buf_mask; +	unsigned int		frame_count;  	int			streaming; +	struct isp_video_buf	*buffers[FIMC_ISP_MAX_BUFS];  	const struct fimc_fmt	*format; +	struct v4l2_pix_format_mplane pixfmt;  }; +/* struct fimc_isp:state bit definitions */ +#define ST_ISP_VID_CAP_BUF_PREP		0 +#define ST_ISP_VID_CAP_STREAMING	1 +  /**   * struct fimc_isp - FIMC-IS ISP data structure   * @pdev: pointer to FIMC-IS platform device diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index 72a343e3b5e..bc3ec7d25a3 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -12,7 +12,7 @@  #include <linux/bitops.h>  #include <linux/delay.h>  #include <linux/io.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "fimc-lite-reg.h"  #include "fimc-lite.h" @@ -133,7 +133,7 @@ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)  	int i = ARRAY_SIZE(src_pixfmt_map);  	u32 cfg; -	while (--i >= 0) { +	while (--i) {  		if (src_pixfmt_map[i][0] == pixelcode)  			break;  	} @@ -240,7 +240,7 @@ static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)  	u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);  	int i = ARRAY_SIZE(pixcode); -	while (--i >= 0) +	while (--i)  		if (pixcode[i][0] == f->fmt->mbus_code)  			break;  	cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index e5798f70d14..a97d2352f1d 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -30,7 +30,7 @@  #include <media/v4l2-mem2mem.h>  #include <media/videobuf2-core.h>  #include <media/videobuf2-dma-contig.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "common.h"  #include "fimc-core.h" @@ -350,14 +350,14 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)  	return 0;  } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q)  {  	struct fimc_lite *fimc = q->drv_priv;  	if (!fimc_lite_active(fimc)) -		return -EINVAL; +		return; -	return fimc_lite_stop_capture(fimc, false); +	fimc_lite_stop_capture(fimc, false);  }  static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, @@ -546,7 +546,7 @@ static int fimc_lite_release(struct file *file)  		mutex_unlock(&entity->parent->graph_mutex);  	} -	vb2_fop_release(file); +	_vb2_fop_release(file, NULL);  	pm_runtime_put(&fimc->pdev->dev);  	clear_bit(ST_FLITE_SUSPENDED, &fimc->state); @@ -1313,7 +1313,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct flite_buffer);  	q->drv_priv = fimc; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	q->lock = &fimc->lock;  	ret = vb2_queue_init(q); @@ -1549,42 +1549,46 @@ static int fimc_lite_probe(struct platform_device *pdev)  			       0, dev_name(dev), fimc);  	if (ret) {  		dev_err(dev, "Failed to install irq (%d)\n", ret); -		goto err_clk; +		goto err_clk_put;  	}  	/* The video node will be created within the subdev's registered() op */  	ret = fimc_lite_create_capture_subdev(fimc);  	if (ret) -		goto err_clk; +		goto err_clk_put;  	platform_set_drvdata(pdev, fimc);  	pm_runtime_enable(dev); -	ret = pm_runtime_get_sync(dev); -	if (ret < 0) -		goto err_sd; + +	if (!pm_runtime_enabled(dev)) { +		ret = clk_enable(fimc->clock); +		if (ret < 0) +			goto err_sd; +	}  	fimc->alloc_ctx = vb2_dma_contig_init_ctx(dev);  	if (IS_ERR(fimc->alloc_ctx)) {  		ret = PTR_ERR(fimc->alloc_ctx); -		goto err_pm; +		goto err_clk_dis;  	} -	pm_runtime_put(dev); -  	fimc_lite_set_default_config(fimc);  	dev_dbg(dev, "FIMC-LITE.%d registered successfully\n",  		fimc->index);  	return 0; -err_pm: -	pm_runtime_put(dev); + +err_clk_dis: +	if (!pm_runtime_enabled(dev)) +		clk_disable(fimc->clock);  err_sd:  	fimc_lite_unregister_capture_subdev(fimc); -err_clk: +err_clk_put:  	fimc_lite_clk_put(fimc);  	return ret;  } +#ifdef CONFIG_PM_RUNTIME  static int fimc_lite_runtime_resume(struct device *dev)  {  	struct fimc_lite *fimc = dev_get_drvdata(dev); @@ -1600,6 +1604,7 @@ static int fimc_lite_runtime_suspend(struct device *dev)  	clk_disable(fimc->clock);  	return 0;  } +#endif  #ifdef CONFIG_PM_SLEEP  static int fimc_lite_resume(struct device *dev) diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 7428b2d22b5..ea19dc7be63 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -23,7 +23,7 @@  #include <media/v4l2-ctrls.h>  #include <media/v4l2-device.h>  #include <media/v4l2-mediabus.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #define FIMC_LITE_DRV_NAME	"exynos-fimc-lite"  #define FLITE_CLK_NAME		"flite" diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index 8d33b68c76b..0ad1b6f84a2 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -44,17 +44,17 @@ void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)  {  	struct vb2_buffer *src_vb, *dst_vb; -	if (!ctx || !ctx->m2m_ctx) +	if (!ctx || !ctx->fh.m2m_ctx)  		return; -	src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); -	dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); +	src_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); +	dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);  	if (src_vb && dst_vb) {  		v4l2_m2m_buf_done(src_vb, vb_state);  		v4l2_m2m_buf_done(dst_vb, vb_state);  		v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, -				    ctx->m2m_ctx); +				    ctx->fh.m2m_ctx);  	}  } @@ -85,7 +85,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)  	return ret > 0 ? 0 : ret;  } -static int stop_streaming(struct vb2_queue *q) +static void stop_streaming(struct vb2_queue *q)  {  	struct fimc_ctx *ctx = q->drv_priv;  	int ret; @@ -95,7 +95,6 @@ static int stop_streaming(struct vb2_queue *q)  		fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);  	pm_runtime_put(&ctx->fimc_dev->pdev->dev); -	return 0;  }  static void fimc_device_run(void *priv) @@ -123,17 +122,20 @@ static void fimc_device_run(void *priv)  		fimc_prepare_dma_offset(ctx, df);  	} -	src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); +	src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);  	ret = fimc_prepare_addr(ctx, src_vb, sf, &sf->paddr);  	if (ret)  		goto dma_unlock; -	dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); +	dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);  	ret = fimc_prepare_addr(ctx, dst_vb, df, &df->paddr);  	if (ret)  		goto dma_unlock;  	dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; +	dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst_vb->v4l2_buf.flags |= +		src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  	/* Reconfigure hardware if the context has changed. */  	if (fimc->m2m.ctx != ctx) { @@ -194,7 +196,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,  	*num_planes = f->fmt->memplanes;  	for (i = 0; i < f->fmt->memplanes; i++) { -		sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; +		sizes[i] = f->payload[i];  		allocators[i] = ctx->fimc_dev->alloc_ctx;  	}  	return 0; @@ -219,31 +221,15 @@ static int fimc_buf_prepare(struct vb2_buffer *vb)  static void fimc_buf_queue(struct vb2_buffer *vb)  {  	struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - -	dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - -	if (ctx->m2m_ctx) -		v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void fimc_lock(struct vb2_queue *vq) -{ -	struct fimc_ctx *ctx = vb2_get_drv_priv(vq); -	mutex_lock(&ctx->fimc_dev->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ -	struct fimc_ctx *ctx = vb2_get_drv_priv(vq); -	mutex_unlock(&ctx->fimc_dev->lock); +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);  }  static struct vb2_ops fimc_qops = {  	.queue_setup	 = fimc_queue_setup,  	.buf_prepare	 = fimc_buf_prepare,  	.buf_queue	 = fimc_buf_queue, -	.wait_prepare	 = fimc_unlock, -	.wait_finish	 = fimc_lock, +	.wait_prepare	 = vb2_ops_wait_prepare, +	.wait_finish	 = vb2_ops_wait_finish,  	.stop_streaming	 = stop_streaming,  	.start_streaming = start_streaming,  }; @@ -355,7 +341,7 @@ static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt,  {  	int i; -	for (i = 0; i < fmt->colplanes; i++) { +	for (i = 0; i < fmt->memplanes; i++) {  		frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline;  		frame->payload[i] = pixm->plane_fmt[i].sizeimage;  	} @@ -385,7 +371,7 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,  	if (ret)  		return ret; -	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);  	if (vb2_is_busy(vq)) {  		v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type); @@ -410,56 +396,6 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,  	return 0;  } -static int fimc_m2m_reqbufs(struct file *file, void *fh, -			    struct v4l2_requestbuffers *reqbufs) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int fimc_m2m_querybuf(struct file *file, void *fh, -			     struct v4l2_buffer *buf) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_qbuf(struct file *file, void *fh, -			 struct v4l2_buffer *buf) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_dqbuf(struct file *file, void *fh, -			  struct v4l2_buffer *buf) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_expbuf(struct file *file, void *fh, -			    struct v4l2_exportbuffer *eb) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); -} - - -static int fimc_m2m_streamon(struct file *file, void *fh, -			     enum v4l2_buf_type type) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_streamoff(struct file *file, void *fh, -			    enum v4l2_buf_type type) -{ -	struct fimc_ctx *ctx = fh_to_ctx(fh); -	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} -  static int fimc_m2m_cropcap(struct file *file, void *fh,  			    struct v4l2_cropcap *cr)  { @@ -524,7 +460,7 @@ static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)  	else  		halign = ffs(fimc->variant->min_vsize_align) - 1; -	for (i = 0; i < f->fmt->colplanes; i++) +	for (i = 0; i < f->fmt->memplanes; i++)  		depth += f->fmt->depth[i];  	v4l_bound_align_image(&cr->c.width, min_size, f->o_width, @@ -598,13 +534,13 @@ static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {  	.vidioc_try_fmt_vid_out_mplane	= fimc_m2m_try_fmt_mplane,  	.vidioc_s_fmt_vid_cap_mplane	= fimc_m2m_s_fmt_mplane,  	.vidioc_s_fmt_vid_out_mplane	= fimc_m2m_s_fmt_mplane, -	.vidioc_reqbufs			= fimc_m2m_reqbufs, -	.vidioc_querybuf		= fimc_m2m_querybuf, -	.vidioc_qbuf			= fimc_m2m_qbuf, -	.vidioc_dqbuf			= fimc_m2m_dqbuf, -	.vidioc_expbuf			= fimc_m2m_expbuf, -	.vidioc_streamon		= fimc_m2m_streamon, -	.vidioc_streamoff		= fimc_m2m_streamoff, +	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs, +	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf, +	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf, +	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf, +	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf, +	.vidioc_streamon		= v4l2_m2m_ioctl_streamon, +	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,  	.vidioc_g_crop			= fimc_m2m_g_crop,  	.vidioc_s_crop			= fimc_m2m_s_crop,  	.vidioc_cropcap			= fimc_m2m_cropcap @@ -623,7 +559,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->ops = &fimc_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops;  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->lock = &ctx->fimc_dev->lock;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -635,7 +572,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->ops = &fimc_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops;  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->lock = &ctx->fimc_dev->lock;  	return vb2_queue_init(dst_vq);  } @@ -708,9 +646,9 @@ static int fimc_m2m_open(struct file *file)  	ctx->out_path = FIMC_IO_DMA;  	ctx->scaler.enabled = 1; -	ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); -	if (IS_ERR(ctx->m2m_ctx)) { -		ret = PTR_ERR(ctx->m2m_ctx); +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); +	if (IS_ERR(ctx->fh.m2m_ctx)) { +		ret = PTR_ERR(ctx->fh.m2m_ctx);  		goto error_c;  	} @@ -725,7 +663,7 @@ static int fimc_m2m_open(struct file *file)  	return 0;  error_m2m_ctx: -	v4l2_m2m_ctx_release(ctx->m2m_ctx); +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);  error_c:  	fimc_ctrls_delete(ctx);  error_fh: @@ -747,7 +685,7 @@ static int fimc_m2m_release(struct file *file)  	mutex_lock(&fimc->lock); -	v4l2_m2m_ctx_release(ctx->m2m_ctx); +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);  	fimc_ctrls_delete(ctx);  	v4l2_fh_del(&ctx->fh);  	v4l2_fh_exit(&ctx->fh); @@ -760,45 +698,13 @@ static int fimc_m2m_release(struct file *file)  	return 0;  } -static unsigned int fimc_m2m_poll(struct file *file, -				  struct poll_table_struct *wait) -{ -	struct fimc_ctx *ctx = fh_to_ctx(file->private_data); -	struct fimc_dev *fimc = ctx->fimc_dev; -	int ret; - -	if (mutex_lock_interruptible(&fimc->lock)) -		return -ERESTARTSYS; - -	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -	mutex_unlock(&fimc->lock); - -	return ret; -} - - -static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) -{ -	struct fimc_ctx *ctx = fh_to_ctx(file->private_data); -	struct fimc_dev *fimc = ctx->fimc_dev; -	int ret; - -	if (mutex_lock_interruptible(&fimc->lock)) -		return -ERESTARTSYS; - -	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -	mutex_unlock(&fimc->lock); - -	return ret; -} -  static const struct v4l2_file_operations fimc_m2m_fops = {  	.owner		= THIS_MODULE,  	.open		= fimc_m2m_open,  	.release	= fimc_m2m_release, -	.poll		= fimc_m2m_poll, +	.poll		= v4l2_m2m_fop_poll,  	.unlocked_ioctl	= video_ioctl2, -	.mmap		= fimc_m2m_mmap, +	.mmap		= v4l2_m2m_fop_mmap,  };  static struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 1db8cb4c46e..2d77fd8f440 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -13,7 +13,7 @@  #include <linux/io.h>  #include <linux/regmap.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "media-dev.h"  #include "fimc-reg.h" diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index a8351127831..344718df5c6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -11,6 +11,8 @@   */  #include <linux/bug.h> +#include <linux/clk.h> +#include <linux/clk-provider.h>  #include <linux/device.h>  #include <linux/errno.h>  #include <linux/i2c.h> @@ -20,14 +22,16 @@  #include <linux/of.h>  #include <linux/of_platform.h>  #include <linux/of_device.h> +#include <linux/of_graph.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h>  #include <linux/types.h>  #include <linux/slab.h> +#include <media/v4l2-async.h>  #include <media/v4l2-ctrls.h>  #include <media/v4l2-of.h>  #include <media/media-device.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "media-dev.h"  #include "fimc-core.h" @@ -35,10 +39,6 @@  #include "fimc-lite.h"  #include "mipi-csis.h" -static int __fimc_md_set_camclk(struct fimc_md *fmd, -				struct fimc_source_info *si, -				bool on); -  /* Set up image sensor subdev -> FIMC capture node notifications. */  static void __setup_sensor_notification(struct fimc_md *fmd,  					struct v4l2_subdev *sensor, @@ -218,17 +218,11 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep,  		if (ret < 0)  			return ret;  	} -	ret = fimc_md_set_camclk(sd, true); -	if (ret < 0) -		goto err_wbclk;  	ret = fimc_pipeline_s_power(p, 1);  	if (!ret)  		return 0; -	fimc_md_set_camclk(sd, false); - -err_wbclk:  	if (!IS_ERR(fmd->wbclk[CLK_IDX_WB_B]) && p->subdevs[IDX_IS_ISP])  		clk_disable_unprepare(fmd->wbclk[CLK_IDX_WB_B]); @@ -254,7 +248,6 @@ static int __fimc_pipeline_close(struct exynos_media_pipeline *ep)  	}  	ret = fimc_pipeline_s_power(p, 0); -	fimc_md_set_camclk(sd, false);  	fmd = entity_to_fimc_mdev(&sd->entity); @@ -332,135 +325,14 @@ static void fimc_md_pipelines_free(struct fimc_md *fmd)  	}  } -/* - * Sensor subdevice helper functions - */ -static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, -						struct fimc_source_info *si) -{ -	struct i2c_adapter *adapter; -	struct v4l2_subdev *sd = NULL; - -	if (!si || !fmd) -		return NULL; -	/* -	 * If FIMC bus type is not Writeback FIFO assume it is same -	 * as sensor_bus_type. -	 */ -	si->fimc_bus_type = si->sensor_bus_type; - -	adapter = i2c_get_adapter(si->i2c_bus_num); -	if (!adapter) { -		v4l2_warn(&fmd->v4l2_dev, -			  "Failed to get I2C adapter %d, deferring probe\n", -			  si->i2c_bus_num); -		return ERR_PTR(-EPROBE_DEFER); -	} -	sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, -						si->board_info, NULL); -	if (IS_ERR_OR_NULL(sd)) { -		i2c_put_adapter(adapter); -		v4l2_warn(&fmd->v4l2_dev, -			  "Failed to acquire subdev %s, deferring probe\n", -			  si->board_info->type); -		return ERR_PTR(-EPROBE_DEFER); -	} -	v4l2_set_subdev_hostdata(sd, si); -	sd->grp_id = GRP_ID_SENSOR; - -	v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", -		  sd->name); -	return sd; -} - -static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) -{ -	struct i2c_client *client = v4l2_get_subdevdata(sd); -	struct i2c_adapter *adapter; - -	if (!client) -		return; - -	v4l2_device_unregister_subdev(sd); - -	if (!client->dev.of_node) { -		adapter = client->adapter; -		i2c_unregister_device(client); -		if (adapter) -			i2c_put_adapter(adapter); -	} -} - -#ifdef CONFIG_OF -/* Register I2C client subdev associated with @node. */ -static int fimc_md_of_add_sensor(struct fimc_md *fmd, -				 struct device_node *node, int index) -{ -	struct fimc_sensor_info *si; -	struct i2c_client *client; -	struct v4l2_subdev *sd; -	int ret; - -	if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) -		return -EINVAL; -	si = &fmd->sensor[index]; - -	client = of_find_i2c_device_by_node(node); -	if (!client) -		return -EPROBE_DEFER; - -	device_lock(&client->dev); - -	if (!client->driver || -	    !try_module_get(client->driver->driver.owner)) { -		ret = -EPROBE_DEFER; -		v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", -						node->full_name); -		goto dev_put; -	} - -	/* Enable sensor's master clock */ -	ret = __fimc_md_set_camclk(fmd, &si->pdata, true); -	if (ret < 0) -		goto mod_put; -	sd = i2c_get_clientdata(client); - -	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); -	__fimc_md_set_camclk(fmd, &si->pdata, false); -	if (ret < 0) -		goto mod_put; - -	v4l2_set_subdev_hostdata(sd, &si->pdata); -	if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) -		sd->grp_id = GRP_ID_FIMC_IS_SENSOR; -	else -		sd->grp_id = GRP_ID_SENSOR; - -	si->subdev = sd; -	v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", -		  sd->name, fmd->num_sensors); -	fmd->num_sensors++; - -mod_put: -	module_put(client->driver->driver.owner); -dev_put: -	device_unlock(&client->dev); -	put_device(&client->dev); -	return ret; -} -  /* Parse port node and register as a sub-device any sensor specified there. */  static int fimc_md_parse_port_node(struct fimc_md *fmd,  				   struct device_node *port,  				   unsigned int index)  { +	struct fimc_source_info *pd = &fmd->sensor[index].pdata;  	struct device_node *rem, *ep, *np; -	struct fimc_source_info *pd;  	struct v4l2_of_endpoint endpoint; -	int ret; -	u32 val; - -	pd = &fmd->sensor[index].pdata;  	/* Assume here a port node can have only one endpoint node. */  	ep = of_get_next_child(port, NULL); @@ -468,38 +340,26 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,  		return 0;  	v4l2_of_parse_endpoint(ep, &endpoint); -	if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS) +	if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS)  		return -EINVAL; -	pd->mux_id = (endpoint.port - 1) & 0x1; +	pd->mux_id = (endpoint.base.port - 1) & 0x1; -	rem = v4l2_of_get_remote_port_parent(ep); +	rem = of_graph_get_remote_port_parent(ep);  	of_node_put(ep);  	if (rem == NULL) {  		v4l2_info(&fmd->v4l2_dev, "Remote device at %s not found\n",  							ep->full_name);  		return 0;  	} -	if (!of_property_read_u32(rem, "samsung,camclk-out", &val)) -		pd->clk_id = val; - -	if (!of_property_read_u32(rem, "clock-frequency", &val)) -		pd->clk_frequency = val; - -	if (pd->clk_frequency == 0) { -		v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", -			 rem->full_name); -		of_node_put(rem); -		return -EINVAL; -	} -	if (fimc_input_is_parallel(endpoint.port)) { +	if (fimc_input_is_parallel(endpoint.base.port)) {  		if (endpoint.bus_type == V4L2_MBUS_PARALLEL)  			pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;  		else  			pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;  		pd->flags = endpoint.bus.parallel.flags; -	} else if (fimc_input_is_mipi_csi(endpoint.port)) { +	} else if (fimc_input_is_mipi_csi(endpoint.base.port)) {  		/*  		 * MIPI CSI-2: only input mux selection and  		 * the sensor's clock frequency is needed. @@ -507,7 +367,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,  		pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;  	} else {  		v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n", -			 endpoint.port, rem->full_name); +			 endpoint.base.port, rem->full_name);  	}  	/*  	 * For FIMC-IS handled sensors, that are placed under i2c-isp device @@ -524,21 +384,40 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,  	else  		pd->fimc_bus_type = pd->sensor_bus_type; -	ret = fimc_md_of_add_sensor(fmd, rem, index); -	of_node_put(rem); +	if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) +		return -EINVAL; -	return ret; +	fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF; +	fmd->sensor[index].asd.match.of.node = rem; +	fmd->async_subdevs[index] = &fmd->sensor[index].asd; + +	fmd->num_sensors++; + +	of_node_put(rem); +	return 0;  }  /* Register all SoC external sub-devices */ -static int fimc_md_of_sensors_register(struct fimc_md *fmd, -				       struct device_node *np) +static int fimc_md_register_sensor_entities(struct fimc_md *fmd)  {  	struct device_node *parent = fmd->pdev->dev.of_node;  	struct device_node *node, *ports;  	int index = 0;  	int ret; +	/* +	 * Runtime resume one of the FIMC entities to make sure +	 * the sclk_cam clocks are not globally disabled. +	 */ +	if (!fmd->pmf) +		return -ENXIO; + +	ret = pm_runtime_get_sync(fmd->pmf); +	if (ret < 0) +		return ret; + +	fmd->num_sensors = 0; +  	/* Attach sensors linked to MIPI CSI-2 receivers */  	for_each_available_child_of_node(parent, node) {  		struct device_node *port; @@ -552,14 +431,14 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd,  		ret = fimc_md_parse_port_node(fmd, port, index);  		if (ret < 0) -			return ret; +			goto rpm_put;  		index++;  	}  	/* Attach sensors listed in the parallel-ports node */  	ports = of_get_child_by_name(parent, "parallel-ports");  	if (!ports) -		return 0; +		goto rpm_put;  	for_each_child_of_node(ports, node) {  		ret = fimc_md_parse_port_node(fmd, node, index); @@ -567,8 +446,9 @@ static int fimc_md_of_sensors_register(struct fimc_md *fmd,  			break;  		index++;  	} - -	return 0; +rpm_put: +	pm_runtime_put(fmd->pmf); +	return ret;  }  static int __of_get_csis_id(struct device_node *np) @@ -581,68 +461,10 @@ static int __of_get_csis_id(struct device_node *np)  	of_property_read_u32(np, "reg", ®);  	return reg - FIMC_INPUT_MIPI_CSI2_0;  } -#else -#define fimc_md_of_sensors_register(fmd, np) (-ENOSYS) -#define __of_get_csis_id(np) (-ENOSYS) -#endif - -static int fimc_md_register_sensor_entities(struct fimc_md *fmd) -{ -	struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; -	struct device_node *of_node = fmd->pdev->dev.of_node; -	int num_clients = 0; -	int ret, i; - -	/* -	 * Runtime resume one of the FIMC entities to make sure -	 * the sclk_cam clocks are not globally disabled. -	 */ -	if (!fmd->pmf) -		return -ENXIO; - -	ret = pm_runtime_get_sync(fmd->pmf); -	if (ret < 0) -		return ret; - -	if (of_node) { -		fmd->num_sensors = 0; -		ret = fimc_md_of_sensors_register(fmd, of_node); -	} else if (pdata) { -		WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor)); -		num_clients = min_t(u32, pdata->num_clients, -				    ARRAY_SIZE(fmd->sensor)); -		fmd->num_sensors = num_clients; - -		for (i = 0; i < num_clients; i++) { -			struct fimc_sensor_info *si = &fmd->sensor[i]; -			struct v4l2_subdev *sd; - -			si->pdata = pdata->source_info[i]; -			ret = __fimc_md_set_camclk(fmd, &si->pdata, true); -			if (ret) -				break; -			sd = fimc_md_register_sensor(fmd, &si->pdata); -			ret = __fimc_md_set_camclk(fmd, &si->pdata, false); - -			if (IS_ERR(sd)) { -				si->subdev = NULL; -				ret = PTR_ERR(sd); -				break; -			} -			si->subdev = sd; -			if (ret) -				break; -		} -	} - -	pm_runtime_put(fmd->pmf); -	return ret; -}  /*   * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration.   */ -  static int register_fimc_lite_entity(struct fimc_md *fmd,  				     struct fimc_lite *fimc_lite)  { @@ -731,8 +553,16 @@ static int register_csis_entity(struct fimc_md *fmd,  static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is)  {  	struct v4l2_subdev *sd = &is->isp.subdev; +	struct exynos_media_pipeline *ep;  	int ret; +	/* Allocate pipeline object for the ISP capture video node. */ +	ep = fimc_md_pipeline_create(fmd); +	if (!ep) +		return -ENOMEM; + +	v4l2_set_subdev_hostdata(sd, ep); +  	ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);  	if (ret) {  		v4l2_err(&fmd->v4l2_dev, @@ -759,7 +589,7 @@ static int fimc_md_register_platform_entity(struct fimc_md *fmd,  		goto dev_unlock;  	drvdata = dev_get_drvdata(dev); -	/* Some subdev didn't probe succesfully id drvdata is NULL */ +	/* Some subdev didn't probe successfully id drvdata is NULL */  	if (drvdata) {  		switch (plat_entity) {  		case IDX_FIMC: @@ -791,35 +621,9 @@ dev_unlock:  	return ret;  } -static int fimc_md_pdev_match(struct device *dev, void *data) -{ -	struct platform_device *pdev = to_platform_device(dev); -	int plat_entity = -1; -	int ret; -	char *p; - -	if (!get_device(dev)) -		return -ENODEV; - -	if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { -		plat_entity = IDX_CSIS; -	} else { -		p = strstr(pdev->name, "fimc"); -		if (p && *(p + 4) == 0) -			plat_entity = IDX_FIMC; -	} - -	if (plat_entity >= 0) -		ret = fimc_md_register_platform_entity(data, pdev, -						       plat_entity); -	put_device(dev); -	return 0; -} -  /* Register FIMC, FIMC-LITE and CSIS media entities */ -#ifdef CONFIG_OF -static int fimc_md_register_of_platform_entities(struct fimc_md *fmd, -						 struct device_node *parent) +static int fimc_md_register_platform_entities(struct fimc_md *fmd, +					      struct device_node *parent)  {  	struct device_node *node;  	int ret = 0; @@ -853,9 +657,6 @@ static int fimc_md_register_of_platform_entities(struct fimc_md *fmd,  	return ret;  } -#else -#define fimc_md_register_of_platform_entities(fmd, node) (-ENOSYS) -#endif  static void fimc_md_unregister_entities(struct fimc_md *fmd)  { @@ -883,12 +684,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)  		v4l2_device_unregister_subdev(fmd->csis[i].sd);  		fmd->csis[i].sd = NULL;  	} -	for (i = 0; i < fmd->num_sensors; i++) { -		if (fmd->sensor[i].subdev == NULL) -			continue; -		fimc_md_unregister_sensor(fmd->sensor[i].subdev); -		fmd->sensor[i].subdev = NULL; -	}  	if (fmd->fimc_is)  		v4l2_device_unregister_subdev(&fmd->fimc_is->isp.subdev); @@ -1004,16 +799,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)  /* Create FIMC-IS links */  static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)  { +	struct fimc_isp *isp = &fmd->fimc_is->isp;  	struct media_entity *source, *sink;  	int i, ret; -	source = &fmd->fimc_is->isp.subdev.entity; +	source = &isp->subdev.entity;  	for (i = 0; i < FIMC_MAX_DEVS; i++) {  		if (fmd->fimc[i] == NULL)  			continue; -		/* Link from IS-ISP subdev to FIMC */ +		/* Link from FIMC-IS-ISP subdev to FIMC */  		sink = &fmd->fimc[i]->vid_cap.subdev.entity;  		ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,  					       sink, FIMC_SD_PAD_SINK_FIFO, 0); @@ -1021,7 +817,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)  			return ret;  	} -	return ret; +	/* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */ +	sink = &isp->video_capture.ve.vdev.entity; + +	/* Skip this link if the fimc-is-isp video node driver isn't built-in */ +	if (sink->num_pads == 0) +		return 0; + +	return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA, +					sink, 0, 0);  }  /** @@ -1164,7 +968,7 @@ static void fimc_md_put_clocks(struct fimc_md *fmd)  static int fimc_md_get_clocks(struct fimc_md *fmd)  { -	struct device *dev = NULL; +	struct device *dev = &fmd->pdev->dev;  	char clk_name[32];  	struct clk *clock;  	int i, ret = 0; @@ -1172,16 +976,12 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)  	for (i = 0; i < FIMC_MAX_CAMCLKS; i++)  		fmd->camclk[i].clock = ERR_PTR(-EINVAL); -	if (fmd->pdev->dev.of_node) -		dev = &fmd->pdev->dev; -  	for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {  		snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);  		clock = clk_get(dev, clk_name);  		if (IS_ERR(clock)) { -			dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", -								clk_name); +			dev_err(dev, "Failed to get clock: %s\n", clk_name);  			ret = PTR_ERR(clock);  			break;  		} @@ -1215,70 +1015,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)  	return ret;  } -static int __fimc_md_set_camclk(struct fimc_md *fmd, -				struct fimc_source_info *si, -				bool on) -{ -	struct fimc_camclk_info *camclk; -	int ret = 0; - -	if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) -		return -EINVAL; - -	camclk = &fmd->camclk[si->clk_id]; - -	dbg("camclk %d, f: %lu, use_count: %d, on: %d", -	    si->clk_id, si->clk_frequency, camclk->use_count, on); - -	if (on) { -		if (camclk->use_count > 0 && -		    camclk->frequency != si->clk_frequency) -			return -EINVAL; - -		if (camclk->use_count++ == 0) { -			clk_set_rate(camclk->clock, si->clk_frequency); -			camclk->frequency = si->clk_frequency; -			ret = pm_runtime_get_sync(fmd->pmf); -			if (ret < 0) -				return ret; -			ret = clk_prepare_enable(camclk->clock); -			dbg("Enabled camclk %d: f: %lu", si->clk_id, -			    clk_get_rate(camclk->clock)); -		} -		return ret; -	} - -	if (WARN_ON(camclk->use_count == 0)) -		return 0; - -	if (--camclk->use_count == 0) { -		clk_disable_unprepare(camclk->clock); -		pm_runtime_put(fmd->pmf); -		dbg("Disabled camclk %d", si->clk_id); -	} -	return ret; -} - -/** - * fimc_md_set_camclk - peripheral sensor clock setup - * @sd: sensor subdev to configure sclk_cam clock for - * @on: 1 to enable or 0 to disable the clock - * - * There are 2 separate clock outputs available in the SoC for external - * image processors. These clocks are shared between all registered FIMC - * devices to which sensors can be attached, either directly or through - * the MIPI CSI receiver. The clock is allowed here to be used by - * multiple sensors concurrently if they use same frequency. - * This function should only be called when the graph mutex is held. - */ -int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) -{ -	struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); -	struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); - -	return __fimc_md_set_camclk(fmd, si, on); -} -  static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)  {  	struct exynos_video_entity *ve; @@ -1437,6 +1173,148 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd)  	return 0;  } +static int cam_clk_prepare(struct clk_hw *hw) +{ +	struct cam_clk *camclk = to_cam_clk(hw); +	int ret; + +	if (camclk->fmd->pmf == NULL) +		return -ENODEV; + +	ret = pm_runtime_get_sync(camclk->fmd->pmf); +	return ret < 0 ? ret : 0; +} + +static void cam_clk_unprepare(struct clk_hw *hw) +{ +	struct cam_clk *camclk = to_cam_clk(hw); + +	if (camclk->fmd->pmf == NULL) +		return; + +	pm_runtime_put_sync(camclk->fmd->pmf); +} + +static const struct clk_ops cam_clk_ops = { +	.prepare = cam_clk_prepare, +	.unprepare = cam_clk_unprepare, +}; + +static void fimc_md_unregister_clk_provider(struct fimc_md *fmd) +{ +	struct cam_clk_provider *cp = &fmd->clk_provider; +	unsigned int i; + +	if (cp->of_node) +		of_clk_del_provider(cp->of_node); + +	for (i = 0; i < cp->num_clocks; i++) +		clk_unregister(cp->clks[i]); +} + +static int fimc_md_register_clk_provider(struct fimc_md *fmd) +{ +	struct cam_clk_provider *cp = &fmd->clk_provider; +	struct device *dev = &fmd->pdev->dev; +	int i, ret; + +	for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { +		struct cam_clk *camclk = &cp->camclk[i]; +		struct clk_init_data init; +		const char *p_name; + +		ret = of_property_read_string_index(dev->of_node, +					"clock-output-names", i, &init.name); +		if (ret < 0) +			break; + +		p_name = __clk_get_name(fmd->camclk[i].clock); + +		/* It's safe since clk_register() will duplicate the string. */ +		init.parent_names = &p_name; +		init.num_parents = 1; +		init.ops = &cam_clk_ops; +		init.flags = CLK_SET_RATE_PARENT; +		camclk->hw.init = &init; +		camclk->fmd = fmd; + +		cp->clks[i] = clk_register(NULL, &camclk->hw); +		if (IS_ERR(cp->clks[i])) { +			dev_err(dev, "failed to register clock: %s (%ld)\n", +					init.name, PTR_ERR(cp->clks[i])); +			ret = PTR_ERR(cp->clks[i]); +			goto err; +		} +		cp->num_clocks++; +	} + +	if (cp->num_clocks == 0) { +		dev_warn(dev, "clk provider not registered\n"); +		return 0; +	} + +	cp->clk_data.clks = cp->clks; +	cp->clk_data.clk_num = cp->num_clocks; +	cp->of_node = dev->of_node; +	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, +				  &cp->clk_data); +	if (ret == 0) +		return 0; +err: +	fimc_md_unregister_clk_provider(fmd); +	return ret; +} + +static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, +				 struct v4l2_subdev *subdev, +				 struct v4l2_async_subdev *asd) +{ +	struct fimc_md *fmd = notifier_to_fimc_md(notifier); +	struct fimc_sensor_info *si = NULL; +	int i; + +	/* Find platform data for this sensor subdev */ +	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) +		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node) +			si = &fmd->sensor[i]; + +	if (si == NULL) +		return -EINVAL; + +	v4l2_set_subdev_hostdata(subdev, &si->pdata); + +	if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) +		subdev->grp_id = GRP_ID_FIMC_IS_SENSOR; +	else +		subdev->grp_id = GRP_ID_SENSOR; + +	si->subdev = subdev; + +	v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", +		  subdev->name, fmd->num_sensors); + +	fmd->num_sensors++; + +	return 0; +} + +static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) +{ +	struct fimc_md *fmd = notifier_to_fimc_md(notifier); +	int ret; + +	mutex_lock(&fmd->media_dev.graph_mutex); + +	ret = fimc_md_create_links(fmd); +	if (ret < 0) +		goto unlock; + +	ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); +unlock: +	mutex_unlock(&fmd->media_dev.graph_mutex); +	return ret; +} +  static int fimc_md_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -1449,8 +1327,8 @@ static int fimc_md_probe(struct platform_device *pdev)  		return -ENOMEM;  	spin_lock_init(&fmd->slock); -	fmd->pdev = pdev;  	INIT_LIST_HEAD(&fmd->pipelines); +	fmd->pdev = pdev;  	strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",  		sizeof(fmd->media_dev.model)); @@ -1463,69 +1341,90 @@ static int fimc_md_probe(struct platform_device *pdev)  	strlcpy(v4l2_dev->name, "s5p-fimc-md", sizeof(v4l2_dev->name));  	fmd->use_isp = fimc_md_is_isp_available(dev->of_node); +	fmd->user_subdev_api = true;  	ret = v4l2_device_register(dev, &fmd->v4l2_dev);  	if (ret < 0) {  		v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);  		return ret;  	} +  	ret = media_device_register(&fmd->media_dev);  	if (ret < 0) {  		v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); -		goto err_md; +		goto err_v4l2_dev;  	} +  	ret = fimc_md_get_clocks(fmd);  	if (ret) -		goto err_clk; - -	fmd->user_subdev_api = (dev->of_node != NULL); - -	/* Protect the media graph while we're registering entities */ -	mutex_lock(&fmd->media_dev.graph_mutex); +		goto err_md;  	ret = fimc_md_get_pinctrl(fmd);  	if (ret < 0) {  		if (ret != EPROBE_DEFER)  			dev_err(dev, "Failed to get pinctrl: %d\n", ret); -		goto err_unlock; +		goto err_clk;  	} -	if (dev->of_node) -		ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); -	else -		ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, -						fimc_md_pdev_match); -	if (ret) -		goto err_unlock; +	platform_set_drvdata(pdev, fmd); -	if (dev->platform_data || dev->of_node) { -		ret = fimc_md_register_sensor_entities(fmd); -		if (ret) -			goto err_unlock; +	/* Protect the media graph while we're registering entities */ +	mutex_lock(&fmd->media_dev.graph_mutex); + +	ret = fimc_md_register_platform_entities(fmd, dev->of_node); +	if (ret) { +		mutex_unlock(&fmd->media_dev.graph_mutex); +		goto err_clk;  	} -	ret = fimc_md_create_links(fmd); -	if (ret) -		goto err_unlock; -	ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); -	if (ret) -		goto err_unlock; +	ret = fimc_md_register_sensor_entities(fmd); +	if (ret) { +		mutex_unlock(&fmd->media_dev.graph_mutex); +		goto err_m_ent; +	} + +	mutex_unlock(&fmd->media_dev.graph_mutex);  	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);  	if (ret) -		goto err_unlock; +		goto err_m_ent; +	/* +	 * FIMC platform devices need to be registered before the sclk_cam +	 * clocks provider, as one of these devices needs to be activated +	 * to enable the clock. +	 */ +	ret = fimc_md_register_clk_provider(fmd); +	if (ret < 0) { +		v4l2_err(v4l2_dev, "clock provider registration failed\n"); +		goto err_attr; +	} + +	if (fmd->num_sensors > 0) { +		fmd->subdev_notifier.subdevs = fmd->async_subdevs; +		fmd->subdev_notifier.num_subdevs = fmd->num_sensors; +		fmd->subdev_notifier.bound = subdev_notifier_bound; +		fmd->subdev_notifier.complete = subdev_notifier_complete; +		fmd->num_sensors = 0; + +		ret = v4l2_async_notifier_register(&fmd->v4l2_dev, +						&fmd->subdev_notifier); +		if (ret) +			goto err_clk_p; +	} -	platform_set_drvdata(pdev, fmd); -	mutex_unlock(&fmd->media_dev.graph_mutex);  	return 0; -err_unlock: -	mutex_unlock(&fmd->media_dev.graph_mutex); +err_clk_p: +	fimc_md_unregister_clk_provider(fmd); +err_attr: +	device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);  err_clk:  	fimc_md_put_clocks(fmd); +err_m_ent:  	fimc_md_unregister_entities(fmd); -	media_device_unregister(&fmd->media_dev);  err_md: +	media_device_unregister(&fmd->media_dev); +err_v4l2_dev:  	v4l2_device_unregister(&fmd->v4l2_dev);  	return ret;  } @@ -1537,12 +1436,16 @@ static int fimc_md_remove(struct platform_device *pdev)  	if (!fmd)  		return 0; +	fimc_md_unregister_clk_provider(fmd); +	v4l2_async_notifier_unregister(&fmd->subdev_notifier); +  	v4l2_device_unregister(&fmd->v4l2_dev);  	device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);  	fimc_md_unregister_entities(fmd);  	fimc_md_pipelines_free(fmd);  	media_device_unregister(&fmd->media_dev);  	fimc_md_put_clocks(fmd); +  	return 0;  } diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 62599fd7756..03214541f14 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -10,6 +10,7 @@  #define FIMC_MDEVICE_H_  #include <linux/clk.h> +#include <linux/clk-provider.h>  #include <linux/platform_device.h>  #include <linux/mutex.h>  #include <linux/of.h> @@ -18,7 +19,7 @@  #include <media/media-entity.h>  #include <media/v4l2-device.h>  #include <media/v4l2-subdev.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include "fimc-core.h"  #include "fimc-lite.h" @@ -31,8 +32,9 @@  #define PINCTRL_STATE_IDLE	"idle" -#define FIMC_MAX_SENSORS	8 +#define FIMC_MAX_SENSORS	4  #define FIMC_MAX_CAMCLKS	2 +#define DEFAULT_SENSOR_CLK_FREQ	24000000U  /* LCD/ISP Writeback clocks (PIXELASYNCMx) */  enum { @@ -78,6 +80,7 @@ struct fimc_camclk_info {  /**   * struct fimc_sensor_info - image data source subdev information   * @pdata: sensor's atrributes passed as media device's platform data + * @asd: asynchronous subdev registration data structure   * @subdev: image sensor v4l2 subdev   * @host: fimc device the sensor is currently linked to   * @@ -85,10 +88,17 @@ struct fimc_camclk_info {   */  struct fimc_sensor_info {  	struct fimc_source_info pdata; +	struct v4l2_async_subdev asd;  	struct v4l2_subdev *subdev;  	struct fimc_dev *host;  }; +struct cam_clk { +	struct clk_hw hw; +	struct fimc_md *fmd; +}; +#define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw) +  /**   * struct fimc_md - fimc media device information   * @csis: MIPI CSIS subdevs data @@ -105,6 +115,7 @@ struct fimc_sensor_info {   * @pinctrl: camera port pinctrl handle   * @state_default: pinctrl default state handle   * @state_idle: pinctrl idle state handle + * @cam_clk_provider: CAMCLK clock provider structure   * @user_subdev_api: true if subdevs are not configured by the host driver   * @slock: spinlock protecting @sensor array   */ @@ -122,13 +133,25 @@ struct fimc_md {  	struct media_device media_dev;  	struct v4l2_device v4l2_dev;  	struct platform_device *pdev; +  	struct fimc_pinctrl {  		struct pinctrl *pinctrl;  		struct pinctrl_state *state_default;  		struct pinctrl_state *state_idle;  	} pinctl; -	bool user_subdev_api; +	struct cam_clk_provider { +		struct clk *clks[FIMC_MAX_CAMCLKS]; +		struct clk_onecell_data clk_data; +		struct device_node *of_node; +		struct cam_clk camclk[FIMC_MAX_CAMCLKS]; +		int num_clocks; +	} clk_provider; + +	struct v4l2_async_notifier subdev_notifier; +	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS]; + +	bool user_subdev_api;  	spinlock_t slock;  	struct list_head pipelines;  }; @@ -145,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)  		container_of(me->parent, struct fimc_md, media_dev);  } +static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) +{ +	return container_of(n, struct fimc_md, subdev_notifier); +} +  static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)  {  	mutex_lock(&ve->vdev.entity.parent->graph_mutex); diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 0914230b42d..ae54ef5f535 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -20,14 +20,15 @@  #include <linux/memory.h>  #include <linux/module.h>  #include <linux/of.h> -#include <linux/platform_data/mipi-csis.h> +#include <linux/of_graph.h> +#include <linux/phy/phy.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h>  #include <linux/regulator/consumer.h>  #include <linux/slab.h>  #include <linux/spinlock.h>  #include <linux/videodev2.h> -#include <media/s5p_fimc.h> +#include <media/exynos-fimc.h>  #include <media/v4l2-of.h>  #include <media/v4l2-subdev.h> @@ -90,7 +91,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");  #define S5PCSIS_INTSRC_ODD_BEFORE	(1 << 29)  #define S5PCSIS_INTSRC_ODD_AFTER	(1 << 28)  #define S5PCSIS_INTSRC_ODD		(0x3 << 28) -#define S5PCSIS_INTSRC_NON_IMAGE_DATA	(0xff << 28) +#define S5PCSIS_INTSRC_NON_IMAGE_DATA	(0xf << 28)  #define S5PCSIS_INTSRC_FRAME_START	(1 << 27)  #define S5PCSIS_INTSRC_FRAME_END	(1 << 26)  #define S5PCSIS_INTSRC_ERR_SOT_HS	(0xf << 12) @@ -180,6 +181,7 @@ struct csis_drvdata {   * @sd: v4l2_subdev associated with CSIS device instance   * @index: the hardware instance index   * @pdev: CSIS platform device + * @phy: pointer to the CSIS generic PHY   * @regs: mmaped I/O registers memory   * @supplies: CSIS regulator supplies   * @clock: CSIS clocks @@ -203,6 +205,7 @@ struct csis_state {  	struct v4l2_subdev sd;  	u8 index;  	struct platform_device *pdev; +	struct phy *phy;  	void __iomem *regs;  	struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];  	struct clk *clock[NUM_CSIS_CLOCKS]; @@ -726,26 +729,6 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static int s5pcsis_get_platform_data(struct platform_device *pdev, -				     struct csis_state *state) -{ -	struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data; - -	if (pdata == NULL) { -		dev_err(&pdev->dev, "Platform data not specified\n"); -		return -EINVAL; -	} - -	state->clk_frequency = pdata->clk_rate; -	state->num_lanes = pdata->lanes; -	state->hs_settle = pdata->hs_settle; -	state->index = max(0, pdev->id); -	state->max_num_lanes = state->index ? CSIS1_MAX_LANES : -					      CSIS0_MAX_LANES; -	return 0; -} - -#ifdef CONFIG_OF  static int s5pcsis_parse_dt(struct platform_device *pdev,  			    struct csis_state *state)  { @@ -759,7 +742,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,  				 &state->max_num_lanes))  		return -EINVAL; -	node = v4l2_of_get_next_endpoint(node, NULL); +	node = of_graph_get_next_endpoint(node, NULL);  	if (!node) {  		dev_err(&pdev->dev, "No port node at %s\n",  				pdev->dev.of_node->full_name); @@ -768,7 +751,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,  	/* Get port node and validate MIPI-CSI channel id. */  	v4l2_of_parse_endpoint(node, &endpoint); -	state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0; +	state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;  	if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)  		return -ENXIO; @@ -779,14 +762,12 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,  					"samsung,csis-wclk");  	state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes; -  	of_node_put(node); +  	return 0;  } -#else -#define s5pcsis_parse_dt(pdev, state) (-ENOSYS) -#endif +static int s5pcsis_pm_resume(struct device *dev, bool runtime);  static const struct of_device_id s5pcsis_of_match[];  static int s5pcsis_probe(struct platform_device *pdev) @@ -807,19 +788,14 @@ static int s5pcsis_probe(struct platform_device *pdev)  	spin_lock_init(&state->slock);  	state->pdev = pdev; -	if (dev->of_node) { -		of_id = of_match_node(s5pcsis_of_match, dev->of_node); -		if (WARN_ON(of_id == NULL)) -			return -EINVAL; - -		drv_data = of_id->data; -		state->interrupt_mask = drv_data->interrupt_mask; +	of_id = of_match_node(s5pcsis_of_match, dev->of_node); +	if (WARN_ON(of_id == NULL)) +		return -EINVAL; -		ret = s5pcsis_parse_dt(pdev, state); -	} else { -		ret = s5pcsis_get_platform_data(pdev, state); -	} +	drv_data = of_id->data; +	state->interrupt_mask = drv_data->interrupt_mask; +	ret = s5pcsis_parse_dt(pdev, state);  	if (ret < 0)  		return ret; @@ -829,6 +805,10 @@ static int s5pcsis_probe(struct platform_device *pdev)  		return -EINVAL;  	} +	state->phy = devm_phy_get(dev, "csis"); +	if (IS_ERR(state->phy)) +		return PTR_ERR(state->phy); +  	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	state->regs = devm_ioremap_resource(dev, mem_res);  	if (IS_ERR(state->regs)) @@ -895,13 +875,21 @@ static int s5pcsis_probe(struct platform_device *pdev)  	/* .. and a pointer to the subdev. */  	platform_set_drvdata(pdev, &state->sd);  	memcpy(state->events, s5pcsis_events, sizeof(state->events)); +  	pm_runtime_enable(dev); +	if (!pm_runtime_enabled(dev)) { +		ret = s5pcsis_pm_resume(dev, true); +		if (ret < 0) +			goto e_m_ent; +	}  	dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",  		 state->num_lanes, state->hs_settle, state->wclk_ext,  		 state->clk_frequency);  	return 0; +e_m_ent: +	media_entity_cleanup(&state->sd.entity);  e_clkdis:  	clk_disable(state->clock[CSIS_CLK_MUX]);  e_clkput: @@ -922,7 +910,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)  	mutex_lock(&state->lock);  	if (state->flags & ST_POWERED) {  		s5pcsis_stop_stream(state); -		ret = s5p_csis_phy_enable(state->index, false); +		ret = phy_power_off(state->phy);  		if (ret)  			goto unlock;  		ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, @@ -958,7 +946,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)  					    state->supplies);  		if (ret)  			goto unlock; -		ret = s5p_csis_phy_enable(state->index, true); +		ret = phy_power_on(state->phy);  		if (!ret) {  			state->flags |= ST_POWERED;  		} else { @@ -1007,7 +995,7 @@ static int s5pcsis_remove(struct platform_device *pdev)  	struct csis_state *state = sd_to_csis_state(sd);  	pm_runtime_disable(&pdev->dev); -	s5pcsis_pm_suspend(&pdev->dev, false); +	s5pcsis_pm_suspend(&pdev->dev, true);  	clk_disable(state->clock[CSIS_CLK_MUX]);  	pm_runtime_set_suspended(&pdev->dev);  	s5pcsis_clk_put(state); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index fe9898ca3c8..d5dc198502e 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -21,6 +21,8 @@  #include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h>  #include <linux/of_platform.h>  #include <linux/slab.h>  #include <media/v4l2-common.h> @@ -962,7 +964,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)  	struct viu_fh *fh = priv;  	fh->dev->std = id; -	decoder_call(fh->dev, core, s_std, id); +	decoder_call(fh->dev, video, s_std, id);  	return 0;  } @@ -1578,7 +1580,7 @@ static int viu_of_probe(struct platform_device *op)  	}  	/* enable VIU clock */ -	clk = devm_clk_get(&op->dev, "viu_clk"); +	clk = devm_clk_get(&op->dev, "ipg");  	if (IS_ERR(clk)) {  		dev_err(&op->dev, "failed to lookup the clock!\n");  		ret = PTR_ERR(clk); diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 540516ca872..c21d14fd61d 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -207,8 +207,11 @@ static void dma_callback(void *data)  	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);  	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); -	src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; -	src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; +	dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; +	dst_vb->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst_vb->v4l2_buf.flags |= +		src_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;  	v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);  	v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); @@ -341,8 +344,7 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,  	ctx->xt->dir = DMA_MEM_TO_MEM;  	ctx->xt->src_sgl = false;  	ctx->xt->dst_sgl = true; -	flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | -		DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP; +	flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;  	tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);  	if (tx == NULL) { @@ -869,7 +871,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	src_vq->ops = &deinterlace_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops; -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	q_data[V4L2_M2M_SRC].fmt = &formats[0];  	q_data[V4L2_M2M_SRC].width = 640;  	q_data[V4L2_M2M_SRC].height = 480; @@ -886,7 +888,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	dst_vq->ops = &deinterlace_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops; -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	q_data[V4L2_M2M_DST].fmt = &formats[0];  	q_data[V4L2_M2M_DST].width = 640;  	q_data[V4L2_M2M_DST].height = 480; @@ -919,7 +921,7 @@ static int deinterlace_open(struct file *file)  		return ret;  	} -	ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + +	ctx->xt = kzalloc(sizeof(struct dma_interleaved_template) +  				sizeof(struct data_chunk), GFP_KERNEL);  	if (!ctx->xt) {  		kfree(ctx); @@ -1084,8 +1086,7 @@ free_dev:  static int deinterlace_remove(struct platform_device *pdev)  { -	struct deinterlace_dev *pcdev = -		(struct deinterlace_dev *)platform_get_drvdata(pdev); +	struct deinterlace_dev *pcdev = platform_get_drvdata(pdev);  	v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);  	v4l2_m2m_release(pcdev->m2m_dev); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 5184887b155..be4b5121210 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1156,7 +1156,7 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)  	return mcam_read_setup(cam);  } -static int mcam_vb_stop_streaming(struct vb2_queue *vq) +static void mcam_vb_stop_streaming(struct vb2_queue *vq)  {  	struct mcam_camera *cam = vb2_get_drv_priv(vq);  	unsigned long flags; @@ -1164,10 +1164,10 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)  	if (cam->state == S_BUFWAIT) {  		/* They never gave us buffers */  		cam->state = S_IDLE; -		return 0; +		return;  	}  	if (cam->state != S_STREAMING) -		return -EINVAL; +		return;  	mcam_ctlr_stop_dma(cam);  	/*  	 * Reset the CCIC PHY after stopping streaming, @@ -1182,7 +1182,6 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)  	spin_lock_irqsave(&cam->dev_lock, flags);  	INIT_LIST_HEAD(&cam->buffers);  	spin_unlock_irqrestore(&cam->dev_lock, flags); -	return 0;  } @@ -1221,16 +1220,16 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)  {  	struct mcam_vb_buffer *mvb = vb_to_mvb(vb);  	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); -	struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0); +	struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);  	struct mcam_dma_desc *desc = mvb->dma_desc;  	struct scatterlist *sg;  	int i; -	mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages, -			DMA_FROM_DEVICE); +	mvb->dma_desc_nent = dma_map_sg(cam->dev, sg_table->sgl, +			sg_table->nents, DMA_FROM_DEVICE);  	if (mvb->dma_desc_nent <= 0)  		return -EIO;  /* Not sure what's right here */ -	for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) { +	for_each_sg(sg_table->sgl, sg, mvb->dma_desc_nent, i) {  		desc->dma_addr = sg_dma_address(sg);  		desc->segment_len = sg_dma_len(sg);  		desc++; @@ -1238,13 +1237,14 @@ static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)  	return 0;  } -static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb) +static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)  {  	struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); -	struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0); +	struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0); -	dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE); -	return 0; +	if (sg_table) +		dma_unmap_sg(cam->dev, sg_table->sgl, +				sg_table->nents, DMA_FROM_DEVICE);  }  static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb) diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index b5a19af5c58..054507f1673 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -142,12 +142,6 @@ static int mmpcam_power_up(struct mcam_camera *mcam)  	struct mmp_camera *cam = mcam_to_cam(mcam);  	struct mmp_camera_platform_data *pdata; -	if (mcam->bus_type == V4L2_MBUS_CSI2) { -		cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); -		if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) -			return PTR_ERR(cam->mipi_clk); -	} -  /*   * Turn on power and clocks to the controller.   */ @@ -186,12 +180,6 @@ static void mmpcam_power_down(struct mcam_camera *mcam)  	gpio_set_value(pdata->sensor_power_gpio, 0);  	gpio_set_value(pdata->sensor_reset_gpio, 0); -	if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) { -		if (cam->mipi_clk) -			devm_clk_put(mcam->dev, cam->mipi_clk); -		cam->mipi_clk = NULL; -	} -  	mcam_clk_disable(mcam);  } @@ -292,8 +280,9 @@ void mmpcam_calc_dphy(struct mcam_camera *mcam)  		return;  	/* get the escape clk, this is hard coded */ +	clk_prepare_enable(cam->mipi_clk);  	tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12; - +	clk_disable_unprepare(cam->mipi_clk);  	/*  	 * dphy[2] - CSI2_DPHY6:  	 * bit 0 ~ bit 7: CK Term Enable @@ -325,19 +314,6 @@ static irqreturn_t mmpcam_irq(int irq, void *data)  	return IRQ_RETVAL(handled);  } -static void mcam_deinit_clk(struct mcam_camera *mcam) -{ -	unsigned int i; - -	for (i = 0; i < NR_MCAM_CLK; i++) { -		if (!IS_ERR(mcam->clk[i])) { -			if (mcam->clk[i]) -				devm_clk_put(mcam->dev, mcam->clk[i]); -		} -		mcam->clk[i] = NULL; -	} -} -  static void mcam_init_clk(struct mcam_camera *mcam)  {  	unsigned int i; @@ -371,7 +347,6 @@ static int mmpcam_probe(struct platform_device *pdev)  	if (cam == NULL)  		return -ENOMEM;  	cam->pdev = pdev; -	cam->mipi_clk = NULL;  	INIT_LIST_HEAD(&cam->devlist);  	mcam = &cam->mcam; @@ -387,6 +362,11 @@ static int mmpcam_probe(struct platform_device *pdev)  	mcam->mclk_div = pdata->mclk_div;  	mcam->bus_type = pdata->bus_type;  	mcam->dphy = pdata->dphy; +	if (mcam->bus_type == V4L2_MBUS_CSI2) { +		cam->mipi_clk = devm_clk_get(mcam->dev, "mipi"); +		if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0)) +			return PTR_ERR(cam->mipi_clk); +	}  	mcam->mipi_enabled = false;  	mcam->lane = pdata->lane;  	mcam->chip_id = MCAM_ARMADA610; @@ -444,7 +424,7 @@ static int mmpcam_probe(struct platform_device *pdev)  	 */  	ret = mmpcam_power_up(mcam);  	if (ret) -		goto out_deinit_clk; +		return ret;  	ret = mccic_register(mcam);  	if (ret)  		goto out_power_down; @@ -469,8 +449,6 @@ out_unregister:  	mccic_shutdown(mcam);  out_power_down:  	mmpcam_power_down(mcam); -out_deinit_clk: -	mcam_deinit_clk(mcam);  	return ret;  } @@ -478,19 +456,10 @@ out_deinit_clk:  static int mmpcam_remove(struct mmp_camera *cam)  {  	struct mcam_camera *mcam = &cam->mcam; -	struct mmp_camera_platform_data *pdata;  	mmpcam_remove_device(cam); -	free_irq(cam->irq, mcam);  	mccic_shutdown(mcam);  	mmpcam_power_down(mcam); -	pdata = cam->pdev->dev.platform_data; -	gpio_free(pdata->sensor_reset_gpio); -	gpio_free(pdata->sensor_power_gpio); -	mcam_deinit_clk(mcam); -	iounmap(cam->power_regs); -	iounmap(mcam->regs); -	kfree(cam);  	return 0;  } diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 6a17676f9d7..0714070ed7f 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -60,9 +60,7 @@ MODULE_PARM_DESC(debug, "activates debug info");  #define MEM2MEM_VID_MEM_LIMIT	(16 * 1024 * 1024)  /* Default transaction time in msec */ -#define MEM2MEM_DEF_TRANSTIME	1000 -/* Default number of buffers per transaction */ -#define MEM2MEM_DEF_TRANSLEN	1 +#define MEM2MEM_DEF_TRANSTIME	40  #define MEM2MEM_COLOR_STEP	(0xff >> 4)  #define MEM2MEM_NUM_TILES	8 @@ -114,6 +112,7 @@ struct m2mtest_q_data {  	unsigned int		width;  	unsigned int		height;  	unsigned int		sizeimage; +	unsigned int		sequence;  	struct m2mtest_fmt	*fmt;  }; @@ -177,8 +176,6 @@ struct m2mtest_ctx {  	enum v4l2_colorspace	colorspace; -	struct v4l2_m2m_ctx	*m2m_ctx; -  	/* Source and destination queue data */  	struct m2mtest_q_data   q_data[2];  }; @@ -238,9 +235,21 @@ static int device_process(struct m2mtest_ctx *ctx,  	bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;  	w = 0; +	out_vb->v4l2_buf.sequence = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++; +	in_vb->v4l2_buf.sequence = q_data->sequence++;  	memcpy(&out_vb->v4l2_buf.timestamp,  			&in_vb->v4l2_buf.timestamp,  			sizeof(struct timeval)); +	if (in_vb->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE) +		memcpy(&out_vb->v4l2_buf.timecode, &in_vb->v4l2_buf.timecode, +			sizeof(struct v4l2_timecode)); +	out_vb->v4l2_buf.field = in_vb->v4l2_buf.field; +	out_vb->v4l2_buf.flags = in_vb->v4l2_buf.flags & +		(V4L2_BUF_FLAG_TIMECODE | +		 V4L2_BUF_FLAG_KEYFRAME | +		 V4L2_BUF_FLAG_PFRAME | +		 V4L2_BUF_FLAG_BFRAME | +		 V4L2_BUF_FLAG_TSTAMP_SRC_MASK);  	switch (ctx->mode) {  	case MEM2MEM_HFLIP | MEM2MEM_VFLIP: @@ -342,8 +351,8 @@ static int job_ready(void *priv)  {  	struct m2mtest_ctx *ctx = priv; -	if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen -	    || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) { +	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen +	    || v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {  		dprintk(ctx->dev, "Not enough buffers available\n");  		return 0;  	} @@ -359,21 +368,6 @@ static void job_abort(void *priv)  	ctx->aborting = 1;  } -static void m2mtest_lock(void *priv) -{ -	struct m2mtest_ctx *ctx = priv; -	struct m2mtest_dev *dev = ctx->dev; -	mutex_lock(&dev->dev_mutex); -} - -static void m2mtest_unlock(void *priv) -{ -	struct m2mtest_ctx *ctx = priv; -	struct m2mtest_dev *dev = ctx->dev; -	mutex_unlock(&dev->dev_mutex); -} - -  /* device_run() - prepares and starts the device   *   * This simulates all the immediate preparations required before starting @@ -386,8 +380,8 @@ static void device_run(void *priv)  	struct m2mtest_dev *dev = ctx->dev;  	struct vb2_buffer *src_buf, *dst_buf; -	src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); -	dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); +	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); +	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);  	device_process(ctx, src_buf, dst_buf); @@ -409,8 +403,8 @@ static void device_isr(unsigned long priv)  		return;  	} -	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); -	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); +	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); +	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);  	curr_ctx->num_processed++; @@ -423,7 +417,7 @@ static void device_isr(unsigned long priv)  	    || curr_ctx->aborting) {  		dprintk(curr_ctx->dev, "Finishing transaction\n");  		curr_ctx->num_processed = 0; -		v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); +		v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->fh.m2m_ctx);  	} else {  		device_run(curr_ctx);  	} @@ -491,7 +485,7 @@ static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)  	struct vb2_queue *vq;  	struct m2mtest_q_data *q_data; -	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);  	if (!vq)  		return -EINVAL; @@ -522,19 +516,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,  static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)  { -	enum v4l2_field field; - -	field = f->fmt.pix.field; - -	if (field == V4L2_FIELD_ANY) -		field = V4L2_FIELD_NONE; -	else if (V4L2_FIELD_NONE != field) -		return -EINVAL; -  	/* V4L2 specification suggests the driver corrects the format struct  	 * if any of the dimensions is unsupported */ -	f->fmt.pix.field = field; -  	if (f->fmt.pix.height < MIN_H)  		f->fmt.pix.height = MIN_H;  	else if (f->fmt.pix.height > MAX_H) @@ -548,6 +531,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)  	f->fmt.pix.width &= ~DIM_ALIGN_MASK;  	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;  	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; +	f->fmt.pix.field = V4L2_FIELD_NONE; +	f->fmt.pix.priv = 0;  	return 0;  } @@ -559,7 +544,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,  	struct m2mtest_ctx *ctx = file2ctx(file);  	fmt = find_format(f); -	if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { +	if (!fmt) { +		f->fmt.pix.pixelformat = formats[0].fourcc; +		fmt = find_format(f); +	} +	if (!(fmt->types & MEM2MEM_CAPTURE)) {  		v4l2_err(&ctx->dev->v4l2_dev,  			 "Fourcc format (0x%08x) invalid.\n",  			 f->fmt.pix.pixelformat); @@ -577,7 +566,11 @@ static int vidioc_try_fmt_vid_out(struct file *file, void *priv,  	struct m2mtest_ctx *ctx = file2ctx(file);  	fmt = find_format(f); -	if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { +	if (!fmt) { +		f->fmt.pix.pixelformat = formats[0].fourcc; +		fmt = find_format(f); +	} +	if (!(fmt->types & MEM2MEM_OUTPUT)) {  		v4l2_err(&ctx->dev->v4l2_dev,  			 "Fourcc format (0x%08x) invalid.\n",  			 f->fmt.pix.pixelformat); @@ -594,7 +587,7 @@ static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)  	struct m2mtest_q_data *q_data;  	struct vb2_queue *vq; -	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);  	if (!vq)  		return -EINVAL; @@ -648,52 +641,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv,  	return ret;  } -static int vidioc_reqbufs(struct file *file, void *priv, -			  struct v4l2_requestbuffers *reqbufs) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int vidioc_querybuf(struct file *file, void *priv, -			   struct v4l2_buffer *buf) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_streamon(struct file *file, void *priv, -			   enum v4l2_buf_type type) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int vidioc_streamoff(struct file *file, void *priv, -			    enum v4l2_buf_type type) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} -  static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)  {  	struct m2mtest_ctx *ctx = @@ -748,14 +695,14 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {  	.vidioc_try_fmt_vid_out	= vidioc_try_fmt_vid_out,  	.vidioc_s_fmt_vid_out	= vidioc_s_fmt_vid_out, -	.vidioc_reqbufs		= vidioc_reqbufs, -	.vidioc_querybuf	= vidioc_querybuf, +	.vidioc_reqbufs		= v4l2_m2m_ioctl_reqbufs, +	.vidioc_querybuf	= v4l2_m2m_ioctl_querybuf, +	.vidioc_qbuf		= v4l2_m2m_ioctl_qbuf, +	.vidioc_dqbuf		= v4l2_m2m_ioctl_dqbuf, -	.vidioc_qbuf		= vidioc_qbuf, -	.vidioc_dqbuf		= vidioc_dqbuf, +	.vidioc_streamon	= v4l2_m2m_ioctl_streamon, +	.vidioc_streamoff	= v4l2_m2m_ioctl_streamoff, -	.vidioc_streamon	= vidioc_streamon, -	.vidioc_streamoff	= vidioc_streamoff,  	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,  	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,  }; @@ -803,6 +750,15 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)  	dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);  	q_data = get_q_data(ctx, vb->vb2_queue->type); +	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { +		if (vb->v4l2_buf.field == V4L2_FIELD_ANY) +			vb->v4l2_buf.field = V4L2_FIELD_NONE; +		if (vb->v4l2_buf.field != V4L2_FIELD_NONE) { +			dprintk(ctx->dev, "%s field isn't supported\n", +					__func__); +			return -EINVAL; +		} +	}  	if (vb2_plane_size(vb, 0) < q_data->sizeimage) {  		dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", @@ -818,27 +774,46 @@ static int m2mtest_buf_prepare(struct vb2_buffer *vb)  static void m2mtest_buf_queue(struct vb2_buffer *vb)  {  	struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); -	v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);  } -static void m2mtest_wait_prepare(struct vb2_queue *q) +static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count)  {  	struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); -	m2mtest_unlock(ctx); +	struct m2mtest_q_data *q_data = get_q_data(ctx, q->type); + +	q_data->sequence = 0; +	return 0;  } -static void m2mtest_wait_finish(struct vb2_queue *q) +static void m2mtest_stop_streaming(struct vb2_queue *q)  {  	struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); -	m2mtest_lock(ctx); +	struct vb2_buffer *vb; +	unsigned long flags; + +	for (;;) { +		if (V4L2_TYPE_IS_OUTPUT(q->type)) +			vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); +		else +			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); +		if (vb == NULL) +			return; +		spin_lock_irqsave(&ctx->dev->irqlock, flags); +		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); +		spin_unlock_irqrestore(&ctx->dev->irqlock, flags); +	}  }  static struct vb2_ops m2mtest_qops = {  	.queue_setup	 = m2mtest_queue_setup,  	.buf_prepare	 = m2mtest_buf_prepare,  	.buf_queue	 = m2mtest_buf_queue, -	.wait_prepare	 = m2mtest_wait_prepare, -	.wait_finish	 = m2mtest_wait_finish, +	.start_streaming = m2mtest_start_streaming, +	.stop_streaming  = m2mtest_stop_streaming, +	.wait_prepare	 = vb2_ops_wait_prepare, +	.wait_finish	 = vb2_ops_wait_finish,  };  static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -847,24 +822,26 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *ds  	int ret;  	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; -	src_vq->io_modes = VB2_MMAP | VB2_DMABUF; +	src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;  	src_vq->drv_priv = ctx;  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	src_vq->ops = &m2mtest_qops;  	src_vq->mem_ops = &vb2_vmalloc_memops; -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->lock = &ctx->dev->dev_mutex;  	ret = vb2_queue_init(src_vq);  	if (ret)  		return ret;  	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; +	dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;  	dst_vq->drv_priv = ctx;  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	dst_vq->ops = &m2mtest_qops;  	dst_vq->mem_ops = &vb2_vmalloc_memops; -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->lock = &ctx->dev->dev_mutex;  	return vb2_queue_init(dst_vq);  } @@ -874,10 +851,10 @@ static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {  	.id = V4L2_CID_TRANS_TIME_MSEC,  	.name = "Transaction Time (msec)",  	.type = V4L2_CTRL_TYPE_INTEGER, -	.def = 1001, +	.def = MEM2MEM_DEF_TRANSTIME,  	.min = 1,  	.max = 10001, -	.step = 100, +	.step = 1,  };  static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = { @@ -936,10 +913,10 @@ static int m2mtest_open(struct file *file)  	ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];  	ctx->colorspace = V4L2_COLORSPACE_REC709; -	ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); -	if (IS_ERR(ctx->m2m_ctx)) { -		rc = PTR_ERR(ctx->m2m_ctx); +	if (IS_ERR(ctx->fh.m2m_ctx)) { +		rc = PTR_ERR(ctx->fh.m2m_ctx);  		v4l2_ctrl_handler_free(hdl);  		kfree(ctx); @@ -949,7 +926,8 @@ static int m2mtest_open(struct file *file)  	v4l2_fh_add(&ctx->fh);  	atomic_inc(&dev->num_inst); -	dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); +	dprintk(dev, "Created instance: %p, m2m_ctx: %p\n", +		ctx, ctx->fh.m2m_ctx);  open_unlock:  	mutex_unlock(&dev->dev_mutex); @@ -967,7 +945,7 @@ static int m2mtest_release(struct file *file)  	v4l2_fh_exit(&ctx->fh);  	v4l2_ctrl_handler_free(&ctx->hdl);  	mutex_lock(&dev->dev_mutex); -	v4l2_m2m_ctx_release(ctx->m2m_ctx); +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);  	mutex_unlock(&dev->dev_mutex);  	kfree(ctx); @@ -976,34 +954,13 @@ static int m2mtest_release(struct file *file)  	return 0;  } -static unsigned int m2mtest_poll(struct file *file, -				 struct poll_table_struct *wait) -{ -	struct m2mtest_ctx *ctx = file2ctx(file); - -	return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -} - -static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) -{ -	struct m2mtest_dev *dev = video_drvdata(file); -	struct m2mtest_ctx *ctx = file2ctx(file); -	int res; - -	if (mutex_lock_interruptible(&dev->dev_mutex)) -		return -ERESTARTSYS; -	res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -	mutex_unlock(&dev->dev_mutex); -	return res; -} -  static const struct v4l2_file_operations m2mtest_fops = {  	.owner		= THIS_MODULE,  	.open		= m2mtest_open,  	.release	= m2mtest_release, -	.poll		= m2mtest_poll, +	.poll		= v4l2_m2m_fop_poll,  	.unlocked_ioctl	= video_ioctl2, -	.mmap		= m2mtest_mmap, +	.mmap		= v4l2_m2m_fop_mmap,  };  static struct video_device m2mtest_videodev = { @@ -1019,8 +976,6 @@ static struct v4l2_m2m_ops m2m_ops = {  	.device_run	= device_run,  	.job_ready	= job_ready,  	.job_abort	= job_abort, -	.lock		= m2mtest_lock, -	.unlock		= m2mtest_unlock,  };  static int m2mtest_probe(struct platform_device *pdev) @@ -1090,8 +1045,7 @@ unreg_dev:  static int m2mtest_remove(struct platform_device *pdev)  { -	struct m2mtest_dev *dev = -		(struct m2mtest_dev *)platform_get_drvdata(pdev); +	struct m2mtest_dev *dev = platform_get_drvdata(pdev);  	v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);  	v4l2_m2m_release(dev->m2m_dev); @@ -1134,4 +1088,3 @@ static int __init m2mtest_init(void)  module_init(m2mtest_init);  module_exit(m2mtest_exit); - diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index c690435853b..fa8f7cabe36 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -207,10 +207,8 @@ struct emmaprp_dev {  	struct mutex		dev_mutex;  	spinlock_t		irqlock; -	int			irq_emma;  	void __iomem		*base_emma;  	struct clk		*clk_emma_ahb, *clk_emma_ipg; -	struct resource		*res_emma;  	struct v4l2_m2m_dev	*m2m_dev;  	struct vb2_alloc_ctx	*alloc_ctx; @@ -377,8 +375,13 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data)  			src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);  			dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); -			src_vb->v4l2_buf.timestamp = dst_vb->v4l2_buf.timestamp; -			src_vb->v4l2_buf.timecode = dst_vb->v4l2_buf.timecode; +			dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; +			dst_vb->v4l2_buf.flags &= +				~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +			dst_vb->v4l2_buf.flags |= +				src_vb->v4l2_buf.flags +				& V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +			dst_vb->v4l2_buf.timecode = src_vb->v4l2_buf.timecode;  			spin_lock_irqsave(&pcdev->irqlock, flags);  			v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); @@ -766,7 +769,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	src_vq->ops = &emmaprp_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops; -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -778,7 +781,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	dst_vq->ops = &emmaprp_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops; -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	return vb2_queue_init(dst_vq);  } @@ -896,9 +899,8 @@ static int emmaprp_probe(struct platform_device *pdev)  {  	struct emmaprp_dev *pcdev;  	struct video_device *vfd; -	struct resource *res_emma; -	int irq_emma; -	int ret; +	struct resource *res; +	int irq, ret;  	pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);  	if (!pcdev) @@ -915,12 +917,10 @@ static int emmaprp_probe(struct platform_device *pdev)  	if (IS_ERR(pcdev->clk_emma_ahb))  		return PTR_ERR(pcdev->clk_emma_ahb); -	irq_emma = platform_get_irq(pdev, 0); -	res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (irq_emma < 0 || res_emma == NULL) { -		dev_err(&pdev->dev, "Missing platform resources data\n"); -		return -ENODEV; -	} +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(pcdev->base_emma)) +		return PTR_ERR(pcdev->base_emma);  	ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);  	if (ret) @@ -947,20 +947,11 @@ static int emmaprp_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, pcdev); -	pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res_emma); -	if (IS_ERR(pcdev->base_emma)) { -		ret = PTR_ERR(pcdev->base_emma); -		goto rel_vdev; -	} - -	pcdev->irq_emma = irq_emma; -	pcdev->res_emma = res_emma; - -	if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq, -			     0, MEM2MEM_NAME, pcdev) < 0) { -		ret = -ENODEV; +	irq = platform_get_irq(pdev, 0); +	ret = devm_request_irq(&pdev->dev, irq, emmaprp_irq, 0, +			       dev_name(&pdev->dev), pcdev); +	if (ret)  		goto rel_vdev; -	}  	pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);  	if (IS_ERR(pcdev->alloc_ctx)) { @@ -994,6 +985,8 @@ rel_vdev:  unreg_dev:  	v4l2_device_unregister(&pcdev->v4l2_dev); +	mutex_destroy(&pcdev->dev_mutex); +  	return ret;  } @@ -1007,6 +1000,7 @@ static int emmaprp_remove(struct platform_device *pdev)  	v4l2_m2m_release(pcdev->m2m_dev);  	vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);  	v4l2_device_unregister(&pcdev->v4l2_dev); +	mutex_destroy(&pcdev->dev_mutex);  	return 0;  } diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index dfd0a21a065..9a726eacb29 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -601,6 +601,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus)  	switch (cur_display->type) {  	case OMAP_DISPLAY_TYPE_DSI:  	case OMAP_DISPLAY_TYPE_DPI: +	case OMAP_DISPLAY_TYPE_DVI:  		if (mgr_id == OMAP_DSS_CHANNEL_LCD)  			irq = DISPC_IRQ_VSYNC;  		else if (mgr_id == OMAP_DSS_CHANNEL_LCD2) diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index cf1c437a868..62e7e5783ce 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -270,7 +270,8 @@ int omap_vout_prepare_vrfb(struct omap_vout_device *vout,  	omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0);  	omap_start_dma(tx->dma_ch); -	interruptible_sleep_on_timeout(&tx->wait, VRFB_TX_TIMEOUT); +	wait_event_interruptible_timeout(tx->wait, tx->tx_status == 1, +					 VRFB_TX_TIMEOUT);  	if (tx->tx_status == 0) {  		omap_stop_dma(tx->dma_ch); diff --git a/drivers/media/platform/omap24xxcam-dma.c b/drivers/media/platform/omap24xxcam-dma.c deleted file mode 100644 index 9c00776d658..00000000000 --- a/drivers/media/platform/omap24xxcam-dma.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam-dma.c - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.com> and - *                    David Cohen <david.cohen@indt.org.br>. - * - * 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 <linux/kernel.h> -#include <linux/io.h> -#include <linux/scatterlist.h> - -#include "omap24xxcam.h" - -/* - * - * DMA hardware. - * - */ - -/* Ack all interrupt on CSR and IRQSTATUS_L0 */ -static void omap24xxcam_dmahw_ack_all(void __iomem *base) -{ -	u32 csr; -	int i; - -	for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) { -		csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i)); -		/* ack interrupt in CSR */ -		omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr); -	} -	omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf); -} - -/* Ack dmach on CSR and IRQSTATUS_L0 */ -static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach) -{ -	u32 csr; - -	csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach)); -	/* ack interrupt in CSR */ -	omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr); -	/* ack interrupt in IRQSTATUS */ -	omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach)); - -	return csr; -} - -static int omap24xxcam_dmahw_running(void __iomem *base, int dmach) -{ -	return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; -} - -static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach, -					     dma_addr_t start, u32 len) -{ -	omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), -			    CAMDMA_CCR_SEL_SRC_DST_SYNC -			    | CAMDMA_CCR_BS -			    | CAMDMA_CCR_DST_AMODE_POST_INC -			    | CAMDMA_CCR_SRC_AMODE_POST_INC -			    | CAMDMA_CCR_FS -			    | CAMDMA_CCR_WR_ACTIVE -			    | CAMDMA_CCR_RD_ACTIVE -			    | CAMDMA_CCR_SYNCHRO_CAMERA); -	omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0); -	omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len); -	omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1); -	omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach), -			    CAMDMA_CSDP_WRITE_MODE_POSTED -			    | CAMDMA_CSDP_DST_BURST_EN_32 -			    | CAMDMA_CSDP_DST_PACKED -			    | CAMDMA_CSDP_SRC_BURST_EN_32 -			    | CAMDMA_CSDP_SRC_PACKED -			    | CAMDMA_CSDP_DATA_TYPE_8BITS); -	omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0); -	omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start); -	omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0); -	omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD); -	omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0); -	omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0); -	omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), -			    CAMDMA_CSR_MISALIGNED_ERR -			    | CAMDMA_CSR_SECURE_ERR -			    | CAMDMA_CSR_TRANS_ERR -			    | CAMDMA_CSR_BLOCK -			    | CAMDMA_CSR_DROP); -	omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), -			    CAMDMA_CICR_MISALIGNED_ERR_IE -			    | CAMDMA_CICR_SECURE_ERR_IE -			    | CAMDMA_CICR_TRANS_ERR_IE -			    | CAMDMA_CICR_BLOCK_IE -			    | CAMDMA_CICR_DROP_IE); -} - -static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach) -{ -	omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), -			    CAMDMA_CCR_SEL_SRC_DST_SYNC -			    | CAMDMA_CCR_BS -			    | CAMDMA_CCR_DST_AMODE_POST_INC -			    | CAMDMA_CCR_SRC_AMODE_POST_INC -			    | CAMDMA_CCR_ENABLE -			    | CAMDMA_CCR_FS -			    | CAMDMA_CCR_SYNCHRO_CAMERA); -} - -static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach, -					     int free_dmach) -{ -	int prev_dmach, ch; - -	if (dmach == 0) -		prev_dmach = NUM_CAMDMA_CHANNELS - 1; -	else -		prev_dmach = dmach - 1; -	omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach), -			    CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach); -	/* Did we chain the DMA transfer before the previous one -	 * finished? -	 */ -	ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS; -	while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch)) -		 & CAMDMA_CCR_ENABLE)) { -		if (ch == dmach) { -			/* The previous transfer has ended and this one -			 * hasn't started, so we must not have chained -			 * to the previous one in time.  We'll have to -			 * start it now. -			 */ -			omap24xxcam_dmahw_transfer_start(base, dmach); -			break; -		} else -			ch = (ch + 1) % NUM_CAMDMA_CHANNELS; -	} -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. The DMA - * controller may not be idle after this routine completes, because - * the completion routines might start new transfers. - */ -static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach) -{ -	/* mask all interrupts from this channel */ -	omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); -	/* unlink this channel */ -	omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0, -			      CAMDMA_CLNK_CTRL_ENABLE_LNK); -	/* disable this channel */ -	omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); -} - -static void omap24xxcam_dmahw_init(void __iomem *base) -{ -	omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, -			    CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY -			    | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE -			    | CAMDMA_OCP_SYSCONFIG_AUTOIDLE); - -	omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10, -			      CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH); - -	omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf); -} - -/* - * - * Individual DMA channel handling. - * - */ - -/* Start a DMA transfer from the camera to memory. - * Returns zero if the transfer was successfully started, or non-zero if all - * DMA channels are already in use or starting is currently inhibited. - */ -static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start, -				 u32 len, dma_callback_t callback, void *arg) -{ -	unsigned long flags; -	int dmach; - -	spin_lock_irqsave(&dma->lock, flags); - -	if (!dma->free_dmach || atomic_read(&dma->dma_stop)) { -		spin_unlock_irqrestore(&dma->lock, flags); -		return -EBUSY; -	} - -	dmach = dma->next_dmach; - -	dma->ch_state[dmach].callback = callback; -	dma->ch_state[dmach].arg = arg; - -	omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len); - -	/* We're ready to start the DMA transfer. */ - -	if (dma->free_dmach < NUM_CAMDMA_CHANNELS) { -		/* A transfer is already in progress, so try to chain to it. */ -		omap24xxcam_dmahw_transfer_chain(dma->base, dmach, -						 dma->free_dmach); -	} else { -		/* No transfer is in progress, so we'll just start this one -		 * now. -		 */ -		omap24xxcam_dmahw_transfer_start(dma->base, dmach); -	} - -	dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS; -	dma->free_dmach--; - -	spin_unlock_irqrestore(&dma->lock, flags); - -	return 0; -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. The DMA - * controller may not be idle after this routine completes, because - * the completion routines might start new transfers. - */ -static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr) -{ -	unsigned long flags; -	int dmach, i, free_dmach; -	dma_callback_t callback; -	void *arg; - -	spin_lock_irqsave(&dma->lock, flags); - -	/* stop any DMA transfers in progress */ -	dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS; -	for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) { -		omap24xxcam_dmahw_abort_ch(dma->base, dmach); -		dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS; -	} - -	/* We have to be careful here because the callback routine -	 * might start a new DMA transfer, and we only want to abort -	 * transfers that were started before this routine was called. -	 */ -	free_dmach = dma->free_dmach; -	while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) && -	       (free_dmach < NUM_CAMDMA_CHANNELS)) { -		dmach = (dma->next_dmach + dma->free_dmach) -			% NUM_CAMDMA_CHANNELS; -		callback = dma->ch_state[dmach].callback; -		arg = dma->ch_state[dmach].arg; -		dma->free_dmach++; -		free_dmach++; -		if (callback) { -			/* leave interrupts disabled during callback */ -			spin_unlock(&dma->lock); -			(*callback) (dma, csr, arg); -			spin_lock(&dma->lock); -		} -	} - -	spin_unlock_irqrestore(&dma->lock, flags); -} - -/* Abort all chained DMA transfers. After all transfers have been - * aborted and the DMA controller is idle, the completion routines for - * any aborted transfers will be called in sequence. If the completion - * routines attempt to start a new DMA transfer it will fail, so the - * DMA controller will be idle after this routine completes. - */ -static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr) -{ -	atomic_inc(&dma->dma_stop); -	omap24xxcam_dma_abort(dma, csr); -	atomic_dec(&dma->dma_stop); -} - -/* Camera DMA interrupt service routine. */ -void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma) -{ -	int dmach; -	dma_callback_t callback; -	void *arg; -	u32 csr; -	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR -		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR -		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - -	spin_lock(&dma->lock); - -	if (dma->free_dmach == NUM_CAMDMA_CHANNELS) { -		/* A camera DMA interrupt occurred while all channels -		 * are idle, so we'll acknowledge the interrupt in the -		 * IRQSTATUS register and exit. -		 */ -		omap24xxcam_dmahw_ack_all(dma->base); -		spin_unlock(&dma->lock); -		return; -	} - -	while (dma->free_dmach < NUM_CAMDMA_CHANNELS) { -		dmach = (dma->next_dmach + dma->free_dmach) -			% NUM_CAMDMA_CHANNELS; -		if (omap24xxcam_dmahw_running(dma->base, dmach)) { -			/* This buffer hasn't finished yet, so we're done. */ -			break; -		} -		csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach); -		if (csr & csr_error) { -			/* A DMA error occurred, so stop all DMA -			 * transfers in progress. -			 */ -			spin_unlock(&dma->lock); -			omap24xxcam_dma_stop(dma, csr); -			return; -		} else { -			callback = dma->ch_state[dmach].callback; -			arg = dma->ch_state[dmach].arg; -			dma->free_dmach++; -			if (callback) { -				spin_unlock(&dma->lock); -				(*callback) (dma, csr, arg); -				spin_lock(&dma->lock); -			} -		} -	} - -	spin_unlock(&dma->lock); - -	omap24xxcam_sgdma_process( -		container_of(dma, struct omap24xxcam_sgdma, dma)); -} - -void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) -{ -	unsigned long flags; - -	spin_lock_irqsave(&dma->lock, flags); - -	omap24xxcam_dmahw_init(dma->base); - -	spin_unlock_irqrestore(&dma->lock, flags); -} - -static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, -				 void __iomem *base) -{ -	int ch; - -	/* group all channels on DMA IRQ0 and unmask irq */ -	spin_lock_init(&dma->lock); -	dma->base = base; -	dma->free_dmach = NUM_CAMDMA_CHANNELS; -	dma->next_dmach = 0; -	for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) { -		dma->ch_state[ch].callback = NULL; -		dma->ch_state[ch].arg = NULL; -	} -} - -/* - * - * Scatter-gather DMA. - * - * High-level DMA construct for transferring whole picture frames to - * memory that is discontinuous. - * - */ - -/* DMA completion routine for the scatter-gather DMA fragments. */ -static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr, -				       void *arg) -{ -	struct omap24xxcam_sgdma *sgdma = -		container_of(dma, struct omap24xxcam_sgdma, dma); -	int sgslot = (int)arg; -	struct sgdma_state *sg_state; -	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR -		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR -		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - -	spin_lock(&sgdma->lock); - -	/* We got an interrupt, we can remove the timer */ -	del_timer(&sgdma->reset_timer); - -	sg_state = sgdma->sg_state + sgslot; -	if (!sg_state->queued_sglist) { -		spin_unlock(&sgdma->lock); -		printk(KERN_ERR "%s: sgdma completed when none queued!\n", -		       __func__); -		return; -	} - -	sg_state->csr |= csr; -	if (!--sg_state->queued_sglist) { -		/* Queue for this sglist is empty, so check to see if we're -		 * done. -		 */ -		if ((sg_state->next_sglist == sg_state->sglen) -		    || (sg_state->csr & csr_error)) { -			sgdma_callback_t callback = sg_state->callback; -			void *arg = sg_state->arg; -			u32 sg_csr = sg_state->csr; -			/* All done with this sglist */ -			sgdma->free_sgdma++; -			if (callback) { -				spin_unlock(&sgdma->lock); -				(*callback) (sgdma, sg_csr, arg); -				return; -			} -		} -	} - -	spin_unlock(&sgdma->lock); -} - -/* Start queued scatter-gather DMA transfers. */ -void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma) -{ -	unsigned long flags; -	int queued_sgdma, sgslot; -	struct sgdma_state *sg_state; -	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR -		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR -		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; - -	spin_lock_irqsave(&sgdma->lock, flags); - -	queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma; -	sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; -	while (queued_sgdma > 0) { -		sg_state = sgdma->sg_state + sgslot; -		while ((sg_state->next_sglist < sg_state->sglen) && -		       !(sg_state->csr & csr_error)) { -			const struct scatterlist *sglist; -			unsigned int len; - -			sglist = sg_state->sglist + sg_state->next_sglist; -			/* try to start the next DMA transfer */ -			if (sg_state->next_sglist + 1 == sg_state->sglen) { -				/* -				 *  On the last sg, we handle the case where -				 *  cam->img.pix.sizeimage % PAGE_ALIGN != 0 -				 */ -				len = sg_state->len - sg_state->bytes_read; -			} else { -				len = sg_dma_len(sglist); -			} - -			if (omap24xxcam_dma_start(&sgdma->dma, -						  sg_dma_address(sglist), -						  len, -						  omap24xxcam_sgdma_callback, -						  (void *)sgslot)) { -				/* DMA start failed */ -				spin_unlock_irqrestore(&sgdma->lock, flags); -				return; -			} else { -				unsigned long expires; -				/* DMA start was successful */ -				sg_state->next_sglist++; -				sg_state->bytes_read += len; -				sg_state->queued_sglist++; - -				/* We start the reset timer */ -				expires = jiffies + HZ; -				mod_timer(&sgdma->reset_timer, expires); -			} -		} -		queued_sgdma--; -		sgslot = (sgslot + 1) % NUM_SG_DMA; -	} - -	spin_unlock_irqrestore(&sgdma->lock, flags); -} - -/* - * Queue a scatter-gather DMA transfer from the camera to memory. - * Returns zero if the transfer was successfully queued, or non-zero - * if all of the scatter-gather slots are already in use. - */ -int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, -			    const struct scatterlist *sglist, int sglen, -			    int len, sgdma_callback_t callback, void *arg) -{ -	unsigned long flags; -	struct sgdma_state *sg_state; - -	if ((sglen < 0) || ((sglen > 0) && !sglist)) -		return -EINVAL; - -	spin_lock_irqsave(&sgdma->lock, flags); - -	if (!sgdma->free_sgdma) { -		spin_unlock_irqrestore(&sgdma->lock, flags); -		return -EBUSY; -	} - -	sg_state = sgdma->sg_state + sgdma->next_sgdma; - -	sg_state->sglist = sglist; -	sg_state->sglen = sglen; -	sg_state->next_sglist = 0; -	sg_state->bytes_read = 0; -	sg_state->len = len; -	sg_state->queued_sglist = 0; -	sg_state->csr = 0; -	sg_state->callback = callback; -	sg_state->arg = arg; - -	sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA; -	sgdma->free_sgdma--; - -	spin_unlock_irqrestore(&sgdma->lock, flags); - -	omap24xxcam_sgdma_process(sgdma); - -	return 0; -} - -/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress. - * Any queued scatter-gather DMA transactions that have not yet been started - * will remain queued.  The DMA controller will be idle after this routine - * completes.  When the scatter-gather queue is restarted, the next - * scatter-gather DMA transfer will begin at the start of a new transaction. - */ -void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) -{ -	unsigned long flags; -	int sgslot; -	struct sgdma_state *sg_state; -	u32 csr = CAMDMA_CSR_TRANS_ERR; - -	/* stop any DMA transfers in progress */ -	omap24xxcam_dma_stop(&sgdma->dma, csr); - -	spin_lock_irqsave(&sgdma->lock, flags); - -	if (sgdma->free_sgdma < NUM_SG_DMA) { -		sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; -		sg_state = sgdma->sg_state + sgslot; -		if (sg_state->next_sglist != 0) { -			/* This DMA transfer was in progress, so abort it. */ -			sgdma_callback_t callback = sg_state->callback; -			void *arg = sg_state->arg; -			sgdma->free_sgdma++; -			if (callback) { -				/* leave interrupts masked */ -				spin_unlock(&sgdma->lock); -				(*callback) (sgdma, csr, arg); -				spin_lock(&sgdma->lock); -			} -		} -	} - -	spin_unlock_irqrestore(&sgdma->lock, flags); -} - -void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, -			    void __iomem *base, -			    void (*reset_callback)(unsigned long data), -			    unsigned long reset_callback_data) -{ -	int sg; - -	spin_lock_init(&sgdma->lock); -	sgdma->free_sgdma = NUM_SG_DMA; -	sgdma->next_sgdma = 0; -	for (sg = 0; sg < NUM_SG_DMA; sg++) { -		sgdma->sg_state[sg].sglen = 0; -		sgdma->sg_state[sg].next_sglist = 0; -		sgdma->sg_state[sg].bytes_read = 0; -		sgdma->sg_state[sg].queued_sglist = 0; -		sgdma->sg_state[sg].csr = 0; -		sgdma->sg_state[sg].callback = NULL; -		sgdma->sg_state[sg].arg = NULL; -	} - -	omap24xxcam_dma_init(&sgdma->dma, base); -	setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data); -} diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c deleted file mode 100644 index d2b440c842b..00000000000 --- a/drivers/media/platform/omap24xxcam.c +++ /dev/null @@ -1,1888 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam.c - * - * OMAP 2 camera block driver. - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007-2008 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.com> - * - * 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 <linux/delay.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/videodev2.h> -#include <linux/pci.h>		/* needed for videobufs */ -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/module.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> - -#include "omap24xxcam.h" - -#define OMAP24XXCAM_VERSION "0.0.1" - -#define RESET_TIMEOUT_NS 10000 - -static void omap24xxcam_reset(struct omap24xxcam_device *cam); -static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam); -static void omap24xxcam_device_unregister(struct v4l2_int_device *s); -static int omap24xxcam_remove(struct platform_device *pdev); - -/* module parameters */ -static int video_nr = -1;	/* video device minor (-1 ==> auto assign) */ -/* - * Maximum amount of memory to use for capture buffers. - * Default is 4800KB, enough to double-buffer SXGA. - */ -static int capture_mem = 1280 * 960 * 2 * 2; - -static struct v4l2_int_device omap24xxcam; - -/* - * - * Clocks. - * - */ - -static void omap24xxcam_clock_put(struct omap24xxcam_device *cam) -{ -	if (cam->ick != NULL && !IS_ERR(cam->ick)) -		clk_put(cam->ick); -	if (cam->fck != NULL && !IS_ERR(cam->fck)) -		clk_put(cam->fck); - -	cam->ick = cam->fck = NULL; -} - -static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) -{ -	int rval = 0; - -	cam->fck = clk_get(cam->dev, "fck"); -	if (IS_ERR(cam->fck)) { -		dev_err(cam->dev, "can't get camera fck"); -		rval = PTR_ERR(cam->fck); -		omap24xxcam_clock_put(cam); -		return rval; -	} - -	cam->ick = clk_get(cam->dev, "ick"); -	if (IS_ERR(cam->ick)) { -		dev_err(cam->dev, "can't get camera ick"); -		rval = PTR_ERR(cam->ick); -		omap24xxcam_clock_put(cam); -	} - -	return rval; -} - -static void omap24xxcam_clock_on(struct omap24xxcam_device *cam) -{ -	clk_enable(cam->fck); -	clk_enable(cam->ick); -} - -static void omap24xxcam_clock_off(struct omap24xxcam_device *cam) -{ -	clk_disable(cam->fck); -	clk_disable(cam->ick); -} - -/* - * - * Camera core - * - */ - -/* - * Set xclk. - * - * To disable xclk, use value zero. - */ -static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam, -				      u32 xclk) -{ -	if (xclk) { -		u32 divisor = CAM_MCLK / xclk; - -		if (divisor == 1) -			omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, -					    CC_CTRL_XCLK, -					    CC_CTRL_XCLK_DIV_BYPASS); -		else -			omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, -					    CC_CTRL_XCLK, divisor); -	} else -		omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, -				    CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW); -} - -static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam) -{ -	/* -	 * Setting the camera core AUTOIDLE bit causes problems with frame -	 * synchronization, so we will clear the AUTOIDLE bit instead. -	 */ -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG, -			    CC_SYSCONFIG_AUTOIDLE); - -	/* program the camera interface DMA packet size */ -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA, -			    CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1)); - -	/* enable camera core error interrupts */ -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE, -			    CC_IRQENABLE_FW_ERR_IRQ -			    | CC_IRQENABLE_FSC_ERR_IRQ -			    | CC_IRQENABLE_SSC_ERR_IRQ -			    | CC_IRQENABLE_FIFO_OF_IRQ); -} - -/* - * Enable the camera core. - * - * Data transfer to the camera DMA starts from next starting frame. - */ -static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam) -{ - -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, -			    cam->cc_ctrl); -} - -/* - * Disable camera core. - * - * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The - * core internal state machines will be reset. Use - * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current - * frame completely. - */ -static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam) -{ -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, -			    CC_CTRL_CC_RST); -} - -/* Interrupt service routine for camera core interrupts. */ -static void omap24xxcam_core_isr(struct omap24xxcam_device *cam) -{ -	u32 cc_irqstatus; -	const u32 cc_irqstatus_err = -		CC_IRQSTATUS_FW_ERR_IRQ -		| CC_IRQSTATUS_FSC_ERR_IRQ -		| CC_IRQSTATUS_SSC_ERR_IRQ -		| CC_IRQSTATUS_FIFO_UF_IRQ -		| CC_IRQSTATUS_FIFO_OF_IRQ; - -	cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET, -					  CC_IRQSTATUS); -	omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS, -			    cc_irqstatus); - -	if (cc_irqstatus & cc_irqstatus_err -	    && !atomic_read(&cam->in_reset)) { -		dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n", -			cc_irqstatus); -		omap24xxcam_reset(cam); -	} -} - -/* - * - * videobuf_buffer handling. - * - * Memory for mmapped videobuf_buffers is not allocated - * conventionally, but by several kmalloc allocations and then - * creating the scatterlist on our own. User-space buffers are handled - * normally. - * - */ - -/* - * Free the memory-mapped buffer memory allocated for a - * videobuf_buffer and the associated scatterlist. - */ -static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb) -{ -	struct videobuf_dmabuf *dma = videobuf_to_dma(vb); -	size_t alloc_size; -	struct page *page; -	int i; - -	if (dma->sglist == NULL) -		return; - -	i = dma->sglen; -	while (i) { -		i--; -		alloc_size = sg_dma_len(&dma->sglist[i]); -		page = sg_page(&dma->sglist[i]); -		do { -			ClearPageReserved(page++); -		} while (alloc_size -= PAGE_SIZE); -		__free_pages(sg_page(&dma->sglist[i]), -			     get_order(sg_dma_len(&dma->sglist[i]))); -	} - -	kfree(dma->sglist); -	dma->sglist = NULL; -} - -/* Release all memory related to the videobuf_queue. */ -static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq) -{ -	int i; - -	mutex_lock(&vbq->vb_lock); - -	for (i = 0; i < VIDEO_MAX_FRAME; i++) { -		if (NULL == vbq->bufs[i]) -			continue; -		if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory) -			continue; -		vbq->ops->buf_release(vbq, vbq->bufs[i]); -		omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); -		kfree(vbq->bufs[i]); -		vbq->bufs[i] = NULL; -	} - -	mutex_unlock(&vbq->vb_lock); - -	videobuf_mmap_free(vbq); -} - -/* - * Allocate physically as contiguous as possible buffer for video - * frame and allocate and build DMA scatter-gather list for it. - */ -static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb) -{ -	unsigned int order; -	size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */ -	struct page *page; -	int max_pages, err = 0, i = 0; -	struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - -	/* -	 * allocate maximum size scatter-gather list. Note this is -	 * overhead. We may not use as many entries as we allocate -	 */ -	max_pages = vb->bsize >> PAGE_SHIFT; -	dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL); -	if (dma->sglist == NULL) { -		err = -ENOMEM; -		goto out; -	} - -	while (size) { -		order = get_order(size); -		/* -		 * do not over-allocate even if we would get larger -		 * contiguous chunk that way -		 */ -		if ((PAGE_SIZE << order) > size) -			order--; - -		/* try to allocate as many contiguous pages as possible */ -		page = alloc_pages(GFP_KERNEL, order); -		/* if allocation fails, try to allocate smaller amount */ -		while (page == NULL) { -			order--; -			page = alloc_pages(GFP_KERNEL, order); -			if (page == NULL && !order) { -				err = -ENOMEM; -				goto out; -			} -		} -		size -= (PAGE_SIZE << order); - -		/* append allocated chunk of pages into scatter-gather list */ -		sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0); -		dma->sglen++; -		i++; - -		alloc_size = (PAGE_SIZE << order); - -		/* clear pages before giving them to user space */ -		memset(page_address(page), 0, alloc_size); - -		/* mark allocated pages reserved */ -		do { -			SetPageReserved(page++); -		} while (alloc_size -= PAGE_SIZE); -	} -	/* -	 * REVISIT: not fully correct to assign nr_pages == sglen but -	 * video-buf is passing nr_pages for e.g. unmap_sg calls -	 */ -	dma->nr_pages = dma->sglen; -	dma->direction = PCI_DMA_FROMDEVICE; - -	return 0; - -out: -	omap24xxcam_vbq_free_mmap_buffer(vb); -	return err; -} - -static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq, -					      unsigned int count) -{ -	int i, err = 0; -	struct omap24xxcam_fh *fh = -		container_of(vbq, struct omap24xxcam_fh, vbq); - -	mutex_lock(&vbq->vb_lock); - -	for (i = 0; i < count; i++) { -		err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]); -		if (err) -			goto out; -		dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n", -			videobuf_to_dma(vbq->bufs[i])->sglen, i); -	} - -	mutex_unlock(&vbq->vb_lock); - -	return 0; -out: -	while (i) { -		i--; -		omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); -	} - -	mutex_unlock(&vbq->vb_lock); - -	return err; -} - -/* - * This routine is called from interrupt context when a scatter-gather DMA - * transfer of a videobuf_buffer completes. - */ -static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, -				     u32 csr, void *arg) -{ -	struct omap24xxcam_device *cam = -		container_of(sgdma, struct omap24xxcam_device, sgdma); -	struct omap24xxcam_fh *fh = cam->streaming->private_data; -	struct videobuf_buffer *vb = (struct videobuf_buffer *)arg; -	const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR -		| CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR -		| CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; -	unsigned long flags; - -	spin_lock_irqsave(&cam->core_enable_disable_lock, flags); -	if (--cam->sgdma_in_queue == 0) -		omap24xxcam_core_disable(cam); -	spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - -	v4l2_get_timestamp(&vb->ts); -	vb->field_count = atomic_add_return(2, &fh->field_count); -	if (csr & csr_error) { -		vb->state = VIDEOBUF_ERROR; -		if (!atomic_read(&fh->cam->in_reset)) { -			dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr); -			omap24xxcam_reset(cam); -		} -	} else -		vb->state = VIDEOBUF_DONE; -	wake_up(&vb->done); -} - -static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, -				    struct videobuf_buffer *vb) -{ -	struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - -	/* wait for buffer, especially to get out of the sgdma queue */ -	videobuf_waiton(vbq, vb, 0, 0); -	if (vb->memory == V4L2_MEMORY_MMAP) { -		dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen, -			     dma->direction); -		dma->direction = DMA_NONE; -	} else { -		videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb)); -		videobuf_dma_free(videobuf_to_dma(vb)); -	} - -	vb->state = VIDEOBUF_NEEDS_INIT; -} - -/* - * Limit the number of available kernel image capture buffers based on the - * number requested, the currently selected image size, and the maximum - * amount of memory permitted for kernel capture buffers. - */ -static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, -				 unsigned int *size) -{ -	struct omap24xxcam_fh *fh = vbq->priv_data; - -	if (*cnt <= 0) -		*cnt = VIDEO_MAX_FRAME;	/* supply a default number of buffers */ - -	if (*cnt > VIDEO_MAX_FRAME) -		*cnt = VIDEO_MAX_FRAME; - -	*size = fh->pix.sizeimage; - -	/* accessing fh->cam->capture_mem is ok, it's constant */ -	if (*size * *cnt > fh->cam->capture_mem) -		*cnt = fh->cam->capture_mem / *size; - -	return 0; -} - -static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq, -				  struct videobuf_dmabuf *dma) -{ -	int err = 0; - -	dma->direction = PCI_DMA_FROMDEVICE; -	if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) { -		kfree(dma->sglist); -		dma->sglist = NULL; -		dma->sglen = 0; -		err = -EIO; -	} - -	return err; -} - -static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq, -				   struct videobuf_buffer *vb, -				   enum v4l2_field field) -{ -	struct omap24xxcam_fh *fh = vbq->priv_data; -	int err = 0; - -	/* -	 * Accessing pix here is okay since it's constant while -	 * streaming is on (and we only get called then). -	 */ -	if (vb->baddr) { -		/* This is a userspace buffer. */ -		if (fh->pix.sizeimage > vb->bsize) { -			/* The buffer isn't big enough. */ -			err = -EINVAL; -		} else -			vb->size = fh->pix.sizeimage; -	} else { -		if (vb->state != VIDEOBUF_NEEDS_INIT) { -			/* -			 * We have a kernel bounce buffer that has -			 * already been allocated. -			 */ -			if (fh->pix.sizeimage > vb->size) { -				/* -				 * The image size has been changed to -				 * a larger size since this buffer was -				 * allocated, so we need to free and -				 * reallocate it. -				 */ -				omap24xxcam_vbq_release(vbq, vb); -				vb->size = fh->pix.sizeimage; -			} -		} else { -			/* We need to allocate a new kernel bounce buffer. */ -			vb->size = fh->pix.sizeimage; -		} -	} - -	if (err) -		return err; - -	vb->width = fh->pix.width; -	vb->height = fh->pix.height; -	vb->field = field; - -	if (vb->state == VIDEOBUF_NEEDS_INIT) { -		if (vb->memory == V4L2_MEMORY_MMAP) -			/* -			 * we have built the scatter-gather list by ourself so -			 * do the scatter-gather mapping as well -			 */ -			err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb)); -		else -			err = videobuf_iolock(vbq, vb, NULL); -	} - -	if (!err) -		vb->state = VIDEOBUF_PREPARED; -	else -		omap24xxcam_vbq_release(vbq, vb); - -	return err; -} - -static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq, -				  struct videobuf_buffer *vb) -{ -	struct omap24xxcam_fh *fh = vbq->priv_data; -	struct omap24xxcam_device *cam = fh->cam; -	enum videobuf_state state = vb->state; -	unsigned long flags; -	int err; - -	/* -	 * FIXME: We're marking the buffer active since we have no -	 * pretty way of marking it active exactly when the -	 * scatter-gather transfer starts. -	 */ -	vb->state = VIDEOBUF_ACTIVE; - -	err = omap24xxcam_sgdma_queue(&fh->cam->sgdma, -				      videobuf_to_dma(vb)->sglist, -				      videobuf_to_dma(vb)->sglen, vb->size, -				      omap24xxcam_vbq_complete, vb); - -	if (!err) { -		spin_lock_irqsave(&cam->core_enable_disable_lock, flags); -		if (++cam->sgdma_in_queue == 1 -		    && !atomic_read(&cam->in_reset)) -			omap24xxcam_core_enable(cam); -		spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); -	} else { -		/* -		 * Oops. We're not supposed to get any errors here. -		 * The only way we could get an error is if we ran out -		 * of scatter-gather DMA slots, but we are supposed to -		 * have at least as many scatter-gather DMA slots as -		 * video buffers so that can't happen. -		 */ -		dev_err(cam->dev, "failed to queue a video buffer for dma!\n"); -		dev_err(cam->dev, "likely a bug in the driver!\n"); -		vb->state = state; -	} -} - -static struct videobuf_queue_ops omap24xxcam_vbq_ops = { -	.buf_setup   = omap24xxcam_vbq_setup, -	.buf_prepare = omap24xxcam_vbq_prepare, -	.buf_queue   = omap24xxcam_vbq_queue, -	.buf_release = omap24xxcam_vbq_release, -}; - -/* - * - * OMAP main camera system - * - */ - -/* - * Reset camera block to power-on state. - */ -static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam) -{ -	int max_loop = RESET_TIMEOUT_NS; - -	/* Reset whole camera subsystem */ -	omap24xxcam_reg_out(cam->mmio_base, -			    CAM_SYSCONFIG, -			    CAM_SYSCONFIG_SOFTRESET); - -	/* Wait till it's finished */ -	while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) -		 & CAM_SYSSTATUS_RESETDONE) -	       && --max_loop) { -		ndelay(1); -	} - -	if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) -	      & CAM_SYSSTATUS_RESETDONE)) -		dev_err(cam->dev, "camera soft reset timeout\n"); -} - -/* - * (Re)initialise the camera block. - */ -static void omap24xxcam_hwinit(struct omap24xxcam_device *cam) -{ -	omap24xxcam_poweron_reset(cam); - -	/* set the camera subsystem autoidle bit */ -	omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG, -			    CAM_SYSCONFIG_AUTOIDLE); - -	/* set the camera MMU autoidle bit */ -	omap24xxcam_reg_out(cam->mmio_base, -			    CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG, -			    CAMMMU_SYSCONFIG_AUTOIDLE); - -	omap24xxcam_core_hwinit(cam); - -	omap24xxcam_dma_hwinit(&cam->sgdma.dma); -} - -/* - * Callback for dma transfer stalling. - */ -static void omap24xxcam_stalled_dma_reset(unsigned long data) -{ -	struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data; - -	if (!atomic_read(&cam->in_reset)) { -		dev_dbg(cam->dev, "dma stalled, resetting camera\n"); -		omap24xxcam_reset(cam); -	} -} - -/* - * Stop capture. Mark we're doing a reset, stop DMA transfers and - * core. (No new scatter-gather transfers will be queued whilst - * in_reset is non-zero.) - * - * If omap24xxcam_capture_stop is called from several places at - * once, only the first call will have an effect. Similarly, the last - * call omap24xxcam_streaming_cont will have effect. - * - * Serialisation is ensured by using cam->core_enable_disable_lock. - */ -static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam) -{ -	unsigned long flags; - -	spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - -	if (atomic_inc_return(&cam->in_reset) != 1) { -		spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); -		return; -	} - -	omap24xxcam_core_disable(cam); - -	spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - -	omap24xxcam_sgdma_sync(&cam->sgdma); -} - -/* - * Reset and continue streaming. - * - * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL - * register is supposed to be sufficient to recover from a camera - * interface error, but it doesn't seem to be enough. If we only do - * that then subsequent image captures are out of sync by either one - * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the - * entire camera subsystem prevents the problem with frame - * synchronization. - */ -static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam) -{ -	unsigned long flags; - -	spin_lock_irqsave(&cam->core_enable_disable_lock, flags); - -	if (atomic_read(&cam->in_reset) != 1) -		goto out; - -	omap24xxcam_hwinit(cam); - -	omap24xxcam_sensor_if_enable(cam); - -	omap24xxcam_sgdma_process(&cam->sgdma); - -	if (cam->sgdma_in_queue) -		omap24xxcam_core_enable(cam); - -out: -	atomic_dec(&cam->in_reset); -	spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); -} - -static ssize_t -omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr, -		char *buf) -{ -	struct omap24xxcam_device *cam = dev_get_drvdata(dev); - -	return sprintf(buf, "%s\n", cam->streaming ?  "active" : "inactive"); -} -static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL); - -/* - * Stop capture and restart it. I.e. reset the camera during use. - */ -static void omap24xxcam_reset(struct omap24xxcam_device *cam) -{ -	omap24xxcam_capture_stop(cam); -	omap24xxcam_capture_cont(cam); -} - -/* - * The main interrupt handler. - */ -static irqreturn_t omap24xxcam_isr(int irq, void *arg) -{ -	struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg; -	u32 irqstatus; -	unsigned int irqhandled = 0; - -	irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS); - -	if (irqstatus & -	    (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1 -	     | CAM_IRQSTATUS_DMA_IRQ0)) { -		omap24xxcam_dma_isr(&cam->sgdma.dma); -		irqhandled = 1; -	} -	if (irqstatus & CAM_IRQSTATUS_CC_IRQ) { -		omap24xxcam_core_isr(cam); -		irqhandled = 1; -	} -	if (irqstatus & CAM_IRQSTATUS_MMU_IRQ) -		dev_err(cam->dev, "unhandled camera MMU interrupt!\n"); - -	return IRQ_RETVAL(irqhandled); -} - -/* - * - * Sensor handling. - * - */ - -/* - * Enable the external sensor interface. Try to negotiate interface - * parameters with the sensor and start using the new ones. The calls - * to sensor_if_enable and sensor_if_disable need not to be balanced. - */ -static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam) -{ -	int rval; -	struct v4l2_ifparm p; - -	rval = vidioc_int_g_ifparm(cam->sdev, &p); -	if (rval) { -		dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval); -		return rval; -	} - -	cam->if_type = p.if_type; - -	cam->cc_ctrl = CC_CTRL_CC_EN; - -	switch (p.if_type) { -	case V4L2_IF_TYPE_BT656: -		if (p.u.bt656.frame_start_on_rising_vs) -			cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO; -		if (p.u.bt656.bt_sync_correct) -			cam->cc_ctrl |= CC_CTRL_BT_CORRECT; -		if (p.u.bt656.swap) -			cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM; -		if (p.u.bt656.latch_clk_inv) -			cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL; -		if (p.u.bt656.nobt_hs_inv) -			cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL; -		if (p.u.bt656.nobt_vs_inv) -			cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL; - -		switch (p.u.bt656.mode) { -		case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT: -			cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8; -			break; -		case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT: -			cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10; -			break; -		case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT: -			cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12; -			break; -		case V4L2_IF_TYPE_BT656_MODE_BT_8BIT: -			cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8; -			break; -		case V4L2_IF_TYPE_BT656_MODE_BT_10BIT: -			cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10; -			break; -		default: -			dev_err(cam->dev, -				"bt656 interface mode %d not supported\n", -				p.u.bt656.mode); -			return -EINVAL; -		} -		/* -		 * The clock rate that the sensor wants has changed. -		 * We have to adjust the xclk from OMAP 2 side to -		 * match the sensor's wish as closely as possible. -		 */ -		if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) { -			u32 xclk = p.u.bt656.clock_curr; -			u32 divisor; - -			if (xclk == 0) -				return -EINVAL; - -			if (xclk > CAM_MCLK) -				xclk = CAM_MCLK; - -			divisor = CAM_MCLK / xclk; -			if (divisor * xclk < CAM_MCLK) -				divisor++; -			if (CAM_MCLK / divisor < p.u.bt656.clock_min -			    && divisor > 1) -				divisor--; -			if (divisor > 30) -				divisor = 30; - -			xclk = CAM_MCLK / divisor; - -			if (xclk < p.u.bt656.clock_min -			    || xclk > p.u.bt656.clock_max) -				return -EINVAL; - -			cam->if_u.bt656.xclk = xclk; -		} -		omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk); -		break; -	default: -		/* FIXME: how about other interfaces? */ -		dev_err(cam->dev, "interface type %d not supported\n", -			p.if_type); -		return -EINVAL; -	} - -	return 0; -} - -static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam) -{ -	switch (cam->if_type) { -	case V4L2_IF_TYPE_BT656: -		omap24xxcam_core_xclk_set(cam, 0); -		break; -	} -} - -/* - * Initialise the sensor hardware. - */ -static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam) -{ -	int err = 0; -	struct v4l2_int_device *sdev = cam->sdev; - -	omap24xxcam_clock_on(cam); -	err = omap24xxcam_sensor_if_enable(cam); -	if (err) { -		dev_err(cam->dev, "sensor interface could not be enabled at " -			"initialisation, %d\n", err); -		cam->sdev = NULL; -		goto out; -	} - -	/* power up sensor during sensor initialization */ -	vidioc_int_s_power(sdev, 1); - -	err = vidioc_int_dev_init(sdev); -	if (err) { -		dev_err(cam->dev, "cannot initialize sensor, error %d\n", err); -		/* Sensor init failed --- it's nonexistent to us! */ -		cam->sdev = NULL; -		goto out; -	} - -	dev_info(cam->dev, "sensor is %s\n", sdev->name); - -out: -	omap24xxcam_sensor_if_disable(cam); -	omap24xxcam_clock_off(cam); - -	vidioc_int_s_power(sdev, 0); - -	return err; -} - -static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam) -{ -	if (cam->sdev) -		vidioc_int_dev_exit(cam->sdev); -} - -static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam) -{ -	omap24xxcam_sensor_if_disable(cam); -	omap24xxcam_clock_off(cam); -	vidioc_int_s_power(cam->sdev, 0); -} - -/* - * Power-up and configure camera sensor. It's ready for capturing now. - */ -static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam) -{ -	int rval; - -	omap24xxcam_clock_on(cam); - -	omap24xxcam_sensor_if_enable(cam); - -	rval = vidioc_int_s_power(cam->sdev, 1); -	if (rval) -		goto out; - -	rval = vidioc_int_init(cam->sdev); -	if (rval) -		goto out; - -	return 0; - -out: -	omap24xxcam_sensor_disable(cam); - -	return rval; -} - -static void omap24xxcam_sensor_reset_work(struct work_struct *work) -{ -	struct omap24xxcam_device *cam = -		container_of(work, struct omap24xxcam_device, -			     sensor_reset_work); - -	if (atomic_read(&cam->reset_disable)) -		return; - -	omap24xxcam_capture_stop(cam); - -	if (vidioc_int_reset(cam->sdev) == 0) { -		vidioc_int_init(cam->sdev); -	} else { -		/* Can't reset it by vidioc_int_reset. */ -		omap24xxcam_sensor_disable(cam); -		omap24xxcam_sensor_enable(cam); -	} - -	omap24xxcam_capture_cont(cam); -} - -/* - * - * IOCTL interface. - * - */ - -static int vidioc_querycap(struct file *file, void *fh, -			   struct v4l2_capability *cap) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; - -	strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver)); -	strlcpy(cap->card, cam->vfd->name, sizeof(cap->card)); -	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - -	return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, -				   struct v4l2_fmtdesc *f) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	rval = vidioc_int_enum_fmt_cap(cam->sdev, f); - -	return rval; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, -				struct v4l2_format *f) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	rval = vidioc_int_g_fmt_cap(cam->sdev, f); -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, -				struct v4l2_format *f) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	if (cam->streaming) { -		rval = -EBUSY; -		goto out; -	} - -	rval = vidioc_int_s_fmt_cap(cam->sdev, f); - -out: -	mutex_unlock(&cam->mutex); - -	if (!rval) { -		mutex_lock(&ofh->vbq.vb_lock); -		ofh->pix = f->fmt.pix; -		mutex_unlock(&ofh->vbq.vb_lock); -	} - -	memset(f, 0, sizeof(*f)); -	vidioc_g_fmt_vid_cap(file, fh, f); - -	return rval; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, -				  struct v4l2_format *f) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	rval = vidioc_int_try_fmt_cap(cam->sdev, f); -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_reqbufs(struct file *file, void *fh, -			  struct v4l2_requestbuffers *b) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	if (cam->streaming) { -		mutex_unlock(&cam->mutex); -		return -EBUSY; -	} - -	omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); -	mutex_unlock(&cam->mutex); - -	rval = videobuf_reqbufs(&ofh->vbq, b); - -	/* -	 * Either videobuf_reqbufs failed or the buffers are not -	 * memory-mapped (which would need special attention). -	 */ -	if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) -		goto out; - -	rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval); -	if (rval) -		omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); - -out: -	return rval; -} - -static int vidioc_querybuf(struct file *file, void *fh, -			   struct v4l2_buffer *b) -{ -	struct omap24xxcam_fh *ofh = fh; - -	return videobuf_querybuf(&ofh->vbq, b); -} - -static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ -	struct omap24xxcam_fh *ofh = fh; - -	return videobuf_qbuf(&ofh->vbq, b); -} - -static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	struct videobuf_buffer *vb; -	int rval; - -videobuf_dqbuf_again: -	rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); -	if (rval) -		goto out; - -	vb = ofh->vbq.bufs[b->index]; - -	mutex_lock(&cam->mutex); -	/* _needs_reset returns -EIO if reset is required. */ -	rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr); -	mutex_unlock(&cam->mutex); -	if (rval == -EIO) -		schedule_work(&cam->sensor_reset_work); -	else -		rval = 0; - -out: -	/* -	 * This is a hack. We don't want to show -EIO to the user -	 * space. Requeue the buffer and try again if we're not doing -	 * this in non-blocking mode. -	 */ -	if (rval == -EIO) { -		videobuf_qbuf(&ofh->vbq, b); -		if (!(file->f_flags & O_NONBLOCK)) -			goto videobuf_dqbuf_again; -		/* -		 * We don't have a videobuf_buffer now --- maybe next -		 * time... -		 */ -		rval = -EAGAIN; -	} - -	return rval; -} - -static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	if (cam->streaming) { -		rval = -EBUSY; -		goto out; -	} - -	rval = omap24xxcam_sensor_if_enable(cam); -	if (rval) { -		dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n"); -		goto out; -	} - -	rval = videobuf_streamon(&ofh->vbq); -	if (!rval) { -		cam->streaming = file; -		sysfs_notify(&cam->dev->kobj, NULL, "streaming"); -	} - -out: -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	struct videobuf_queue *q = &ofh->vbq; -	int rval; - -	atomic_inc(&cam->reset_disable); - -	flush_work(&cam->sensor_reset_work); - -	rval = videobuf_streamoff(q); -	if (!rval) { -		mutex_lock(&cam->mutex); -		cam->streaming = NULL; -		mutex_unlock(&cam->mutex); -		sysfs_notify(&cam->dev->kobj, NULL, "streaming"); -	} - -	atomic_dec(&cam->reset_disable); - -	return rval; -} - -static int vidioc_enum_input(struct file *file, void *fh, -			     struct v4l2_input *inp) -{ -	if (inp->index > 0) -		return -EINVAL; - -	strlcpy(inp->name, "camera", sizeof(inp->name)); -	inp->type = V4L2_INPUT_TYPE_CAMERA; - -	return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ -	*i = 0; - -	return 0; -} - -static int vidioc_s_input(struct file *file, void *fh, unsigned int i) -{ -	if (i > 0) -		return -EINVAL; - -	return 0; -} - -static int vidioc_queryctrl(struct file *file, void *fh, -			    struct v4l2_queryctrl *a) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	rval = vidioc_int_queryctrl(cam->sdev, a); - -	return rval; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, -			 struct v4l2_control *a) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	rval = vidioc_int_g_ctrl(cam->sdev, a); -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, -			 struct v4l2_control *a) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	rval = vidioc_int_s_ctrl(cam->sdev, a); -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_g_parm(struct file *file, void *fh, -			 struct v4l2_streamparm *a) { -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	int rval; - -	mutex_lock(&cam->mutex); -	rval = vidioc_int_g_parm(cam->sdev, a); -	mutex_unlock(&cam->mutex); - -	return rval; -} - -static int vidioc_s_parm(struct file *file, void *fh, -			 struct v4l2_streamparm *a) -{ -	struct omap24xxcam_fh *ofh = fh; -	struct omap24xxcam_device *cam = ofh->cam; -	struct v4l2_streamparm old_streamparm; -	int rval; - -	mutex_lock(&cam->mutex); -	if (cam->streaming) { -		rval = -EBUSY; -		goto out; -	} - -	old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -	rval = vidioc_int_g_parm(cam->sdev, &old_streamparm); -	if (rval) -		goto out; - -	rval = vidioc_int_s_parm(cam->sdev, a); -	if (rval) -		goto out; - -	rval = omap24xxcam_sensor_if_enable(cam); -	/* -	 * Revert to old streaming parameters if enabling sensor -	 * interface with the new ones failed. -	 */ -	if (rval) -		vidioc_int_s_parm(cam->sdev, &old_streamparm); - -out: -	mutex_unlock(&cam->mutex); - -	return rval; -} - -/* - * - * File operations. - * - */ - -static unsigned int omap24xxcam_poll(struct file *file, -				     struct poll_table_struct *wait) -{ -	struct omap24xxcam_fh *fh = file->private_data; -	struct omap24xxcam_device *cam = fh->cam; -	struct videobuf_buffer *vb; - -	mutex_lock(&cam->mutex); -	if (cam->streaming != file) { -		mutex_unlock(&cam->mutex); -		return POLLERR; -	} -	mutex_unlock(&cam->mutex); - -	mutex_lock(&fh->vbq.vb_lock); -	if (list_empty(&fh->vbq.stream)) { -		mutex_unlock(&fh->vbq.vb_lock); -		return POLLERR; -	} -	vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); -	mutex_unlock(&fh->vbq.vb_lock); - -	poll_wait(file, &vb->done, wait); - -	if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) -		return POLLIN | POLLRDNORM; - -	return 0; -} - -static int omap24xxcam_mmap_buffers(struct file *file, -				    struct vm_area_struct *vma) -{ -	struct omap24xxcam_fh *fh = file->private_data; -	struct omap24xxcam_device *cam = fh->cam; -	struct videobuf_queue *vbq = &fh->vbq; -	unsigned int first, last, size, i, j; -	int err = 0; - -	mutex_lock(&cam->mutex); -	if (cam->streaming) { -		mutex_unlock(&cam->mutex); -		return -EBUSY; -	} -	mutex_unlock(&cam->mutex); -	mutex_lock(&vbq->vb_lock); - -	/* look for first buffer to map */ -	for (first = 0; first < VIDEO_MAX_FRAME; first++) { -		if (NULL == vbq->bufs[first]) -			continue; -		if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory) -			continue; -		if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) -			break; -	} - -	/* look for last buffer to map */ -	for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { -		if (NULL == vbq->bufs[last]) -			continue; -		if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory) -			continue; -		size += vbq->bufs[last]->bsize; -		if (size == (vma->vm_end - vma->vm_start)) -			break; -	} - -	size = 0; -	for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) { -		struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]); - -		for (j = 0; j < dma->sglen; j++) { -			err = remap_pfn_range( -				vma, vma->vm_start + size, -				page_to_pfn(sg_page(&dma->sglist[j])), -				sg_dma_len(&dma->sglist[j]), vma->vm_page_prot); -			if (err) -				goto out; -			size += sg_dma_len(&dma->sglist[j]); -		} -	} - -out: -	mutex_unlock(&vbq->vb_lock); - -	return err; -} - -static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma) -{ -	struct omap24xxcam_fh *fh = file->private_data; -	int rval; - -	/* let the video-buf mapper check arguments and set-up structures */ -	rval = videobuf_mmap_mapper(&fh->vbq, vma); -	if (rval) -		return rval; - -	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - -	/* do mapping to our allocated buffers */ -	rval = omap24xxcam_mmap_buffers(file, vma); -	/* -	 * In case of error, free vma->vm_private_data allocated by -	 * videobuf_mmap_mapper. -	 */ -	if (rval) -		kfree(vma->vm_private_data); - -	return rval; -} - -static int omap24xxcam_open(struct file *file) -{ -	struct omap24xxcam_device *cam = omap24xxcam.priv; -	struct omap24xxcam_fh *fh; -	struct v4l2_format format; - -	if (!cam || !cam->vfd) -		return -ENODEV; - -	fh = kzalloc(sizeof(*fh), GFP_KERNEL); -	if (fh == NULL) -		return -ENOMEM; - -	mutex_lock(&cam->mutex); -	if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) { -		mutex_unlock(&cam->mutex); -		goto out_try_module_get; -	} - -	if (atomic_inc_return(&cam->users) == 1) { -		omap24xxcam_hwinit(cam); -		if (omap24xxcam_sensor_enable(cam)) { -			mutex_unlock(&cam->mutex); -			goto out_omap24xxcam_sensor_enable; -		} -	} -	mutex_unlock(&cam->mutex); - -	fh->cam = cam; -	mutex_lock(&cam->mutex); -	vidioc_int_g_fmt_cap(cam->sdev, &format); -	mutex_unlock(&cam->mutex); -	/* FIXME: how about fh->pix when there are more users? */ -	fh->pix = format.fmt.pix; - -	file->private_data = fh; - -	spin_lock_init(&fh->vbq_lock); - -	videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL, -				&fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, -				V4L2_FIELD_NONE, -				sizeof(struct videobuf_buffer), fh, NULL); - -	return 0; - -out_omap24xxcam_sensor_enable: -	omap24xxcam_poweron_reset(cam); -	module_put(cam->sdev->module); - -out_try_module_get: -	kfree(fh); - -	return -ENODEV; -} - -static int omap24xxcam_release(struct file *file) -{ -	struct omap24xxcam_fh *fh = file->private_data; -	struct omap24xxcam_device *cam = fh->cam; - -	atomic_inc(&cam->reset_disable); - -	flush_work(&cam->sensor_reset_work); - -	/* stop streaming capture */ -	videobuf_streamoff(&fh->vbq); - -	mutex_lock(&cam->mutex); -	if (cam->streaming == file) { -		cam->streaming = NULL; -		mutex_unlock(&cam->mutex); -		sysfs_notify(&cam->dev->kobj, NULL, "streaming"); -	} else { -		mutex_unlock(&cam->mutex); -	} - -	atomic_dec(&cam->reset_disable); - -	omap24xxcam_vbq_free_mmap_buffers(&fh->vbq); - -	/* -	 * Make sure the reset work we might have scheduled is not -	 * pending! It may be run *only* if we have users. (And it may -	 * not be scheduled anymore since streaming is already -	 * disabled.) -	 */ -	flush_work(&cam->sensor_reset_work); - -	mutex_lock(&cam->mutex); -	if (atomic_dec_return(&cam->users) == 0) { -		omap24xxcam_sensor_disable(cam); -		omap24xxcam_poweron_reset(cam); -	} -	mutex_unlock(&cam->mutex); - -	file->private_data = NULL; - -	module_put(cam->sdev->module); -	kfree(fh); - -	return 0; -} - -static struct v4l2_file_operations omap24xxcam_fops = { -	.ioctl	 = video_ioctl2, -	.poll	 = omap24xxcam_poll, -	.mmap	 = omap24xxcam_mmap, -	.open	 = omap24xxcam_open, -	.release = omap24xxcam_release, -}; - -/* - * - * Power management. - * - */ - -#ifdef CONFIG_PM -static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state) -{ -	struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - -	if (atomic_read(&cam->users) == 0) -		return 0; - -	if (!atomic_read(&cam->reset_disable)) -		omap24xxcam_capture_stop(cam); - -	omap24xxcam_sensor_disable(cam); -	omap24xxcam_poweron_reset(cam); - -	return 0; -} - -static int omap24xxcam_resume(struct platform_device *pdev) -{ -	struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - -	if (atomic_read(&cam->users) == 0) -		return 0; - -	omap24xxcam_hwinit(cam); -	omap24xxcam_sensor_enable(cam); - -	if (!atomic_read(&cam->reset_disable)) -		omap24xxcam_capture_cont(cam); - -	return 0; -} -#endif /* CONFIG_PM */ - -static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = { -	.vidioc_querycap	= vidioc_querycap, -	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt_vid_cap, -	.vidioc_g_fmt_vid_cap	= vidioc_g_fmt_vid_cap, -	.vidioc_s_fmt_vid_cap	= vidioc_s_fmt_vid_cap, -	.vidioc_try_fmt_vid_cap	= vidioc_try_fmt_vid_cap, -	.vidioc_reqbufs		= vidioc_reqbufs, -	.vidioc_querybuf	= vidioc_querybuf, -	.vidioc_qbuf		= vidioc_qbuf, -	.vidioc_dqbuf		= vidioc_dqbuf, -	.vidioc_streamon	= vidioc_streamon, -	.vidioc_streamoff	= vidioc_streamoff, -	.vidioc_enum_input	= vidioc_enum_input, -	.vidioc_g_input		= vidioc_g_input, -	.vidioc_s_input		= vidioc_s_input, -	.vidioc_queryctrl	= vidioc_queryctrl, -	.vidioc_g_ctrl		= vidioc_g_ctrl, -	.vidioc_s_ctrl		= vidioc_s_ctrl, -	.vidioc_g_parm		= vidioc_g_parm, -	.vidioc_s_parm		= vidioc_s_parm, -}; - -/* - * - * Camera device (i.e. /dev/video). - * - */ - -static int omap24xxcam_device_register(struct v4l2_int_device *s) -{ -	struct omap24xxcam_device *cam = s->u.slave->master->priv; -	struct video_device *vfd; -	int rval; - -	/* We already have a slave. */ -	if (cam->sdev) -		return -EBUSY; - -	cam->sdev = s; - -	if (device_create_file(cam->dev, &dev_attr_streaming) != 0) { -		dev_err(cam->dev, "could not register sysfs entry\n"); -		rval = -EBUSY; -		goto err; -	} - -	/* initialize the video_device struct */ -	vfd = cam->vfd = video_device_alloc(); -	if (!vfd) { -		dev_err(cam->dev, "could not allocate video device struct\n"); -		rval = -ENOMEM; -		goto err; -	} -	vfd->release = video_device_release; - -	vfd->v4l2_dev = &cam->v4l2_dev; - -	strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); -	vfd->fops		 = &omap24xxcam_fops; -	vfd->ioctl_ops		 = &omap24xxcam_ioctl_fops; - -	omap24xxcam_hwinit(cam); - -	rval = omap24xxcam_sensor_init(cam); -	if (rval) -		goto err; - -	if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) { -		dev_err(cam->dev, "could not register V4L device\n"); -		rval = -EBUSY; -		goto err; -	} - -	omap24xxcam_poweron_reset(cam); - -	dev_info(cam->dev, "registered device %s\n", -		 video_device_node_name(vfd)); - -	return 0; - -err: -	omap24xxcam_device_unregister(s); - -	return rval; -} - -static void omap24xxcam_device_unregister(struct v4l2_int_device *s) -{ -	struct omap24xxcam_device *cam = s->u.slave->master->priv; - -	omap24xxcam_sensor_exit(cam); - -	if (cam->vfd) { -		if (!video_is_registered(cam->vfd)) { -			/* -			 * The device was never registered, so release the -			 * video_device struct directly. -			 */ -			video_device_release(cam->vfd); -		} else { -			/* -			 * The unregister function will release the -			 * video_device struct as well as -			 * unregistering it. -			 */ -			video_unregister_device(cam->vfd); -		} -		cam->vfd = NULL; -	} - -	device_remove_file(cam->dev, &dev_attr_streaming); - -	cam->sdev = NULL; -} - -static struct v4l2_int_master omap24xxcam_master = { -	.attach = omap24xxcam_device_register, -	.detach = omap24xxcam_device_unregister, -}; - -static struct v4l2_int_device omap24xxcam = { -	.module	= THIS_MODULE, -	.name	= CAM_NAME, -	.type	= v4l2_int_type_master, -	.u	= { -		.master = &omap24xxcam_master -	}, -}; - -/* - * - * Driver initialisation and deinitialisation. - * - */ - -static int omap24xxcam_probe(struct platform_device *pdev) -{ -	struct omap24xxcam_device *cam; -	struct resource *mem; -	int irq; - -	cam = kzalloc(sizeof(*cam), GFP_KERNEL); -	if (!cam) { -		dev_err(&pdev->dev, "could not allocate memory\n"); -		goto err; -	} - -	platform_set_drvdata(pdev, cam); - -	cam->dev = &pdev->dev; - -	if (v4l2_device_register(&pdev->dev, &cam->v4l2_dev)) { -		dev_err(&pdev->dev, "v4l2_device_register failed\n"); -		goto err; -	} - -	/* -	 * Impose a lower limit on the amount of memory allocated for -	 * capture. We require at least enough memory to double-buffer -	 * QVGA (300KB). -	 */ -	if (capture_mem < 320 * 240 * 2 * 2) -		capture_mem = 320 * 240 * 2 * 2; -	cam->capture_mem = capture_mem; - -	/* request the mem region for the camera registers */ -	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!mem) { -		dev_err(cam->dev, "no mem resource?\n"); -		goto err; -	} -	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { -		dev_err(cam->dev, -			"cannot reserve camera register I/O region\n"); -		goto err; -	} -	cam->mmio_base_phys = mem->start; -	cam->mmio_size = resource_size(mem); - -	/* map the region */ -	cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); -	if (!cam->mmio_base) { -		dev_err(cam->dev, "cannot map camera register I/O region\n"); -		goto err; -	} - -	irq = platform_get_irq(pdev, 0); -	if (irq <= 0) { -		dev_err(cam->dev, "no irq for camera?\n"); -		goto err; -	} - -	/* install the interrupt service routine */ -	if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) { -		dev_err(cam->dev, -			"could not install interrupt service routine\n"); -		goto err; -	} -	cam->irq = irq; - -	if (omap24xxcam_clock_get(cam)) -		goto err; - -	INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work); - -	mutex_init(&cam->mutex); -	spin_lock_init(&cam->core_enable_disable_lock); - -	omap24xxcam_sgdma_init(&cam->sgdma, -			       cam->mmio_base + CAMDMA_REG_OFFSET, -			       omap24xxcam_stalled_dma_reset, -			       (unsigned long)cam); - -	omap24xxcam.priv = cam; - -	if (v4l2_int_device_register(&omap24xxcam)) -		goto err; - -	return 0; - -err: -	omap24xxcam_remove(pdev); -	return -ENODEV; -} - -static int omap24xxcam_remove(struct platform_device *pdev) -{ -	struct omap24xxcam_device *cam = platform_get_drvdata(pdev); - -	if (!cam) -		return 0; - -	if (omap24xxcam.priv != NULL) -		v4l2_int_device_unregister(&omap24xxcam); -	omap24xxcam.priv = NULL; - -	omap24xxcam_clock_put(cam); - -	if (cam->irq) { -		free_irq(cam->irq, cam); -		cam->irq = 0; -	} - -	if (cam->mmio_base) { -		iounmap((void *)cam->mmio_base); -		cam->mmio_base = 0; -	} - -	if (cam->mmio_base_phys) { -		release_mem_region(cam->mmio_base_phys, cam->mmio_size); -		cam->mmio_base_phys = 0; -	} - -	v4l2_device_unregister(&cam->v4l2_dev); - -	kfree(cam); - -	return 0; -} - -static struct platform_driver omap24xxcam_driver = { -	.probe	 = omap24xxcam_probe, -	.remove	 = omap24xxcam_remove, -#ifdef CONFIG_PM -	.suspend = omap24xxcam_suspend, -	.resume	 = omap24xxcam_resume, -#endif -	.driver	 = { -		.name = CAM_NAME, -		.owner = THIS_MODULE, -	}, -}; - -module_platform_driver(omap24xxcam_driver); - -MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); -MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(OMAP24XXCAM_VERSION); -module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr, -		 "Minor number for video device (-1 ==> auto assign)"); -module_param(capture_mem, int, 0); -MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture " -		 "buffers (default 4800kiB)"); diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h deleted file mode 100644 index 7f6f7915553..00000000000 --- a/drivers/media/platform/omap24xxcam.h +++ /dev/null @@ -1,596 +0,0 @@ -/* - * drivers/media/platform/omap24xxcam.h - * - * Copyright (C) 2004 MontaVista Software, Inc. - * Copyright (C) 2004 Texas Instruments. - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * Based on code from Andy Lowe <source@mvista.com>. - * - * 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 OMAP24XXCAM_H -#define OMAP24XXCAM_H - -#include <media/videobuf-dma-sg.h> -#include <media/v4l2-int-device.h> -#include <media/v4l2-device.h> - -/* - * - * General driver related definitions. - * - */ - -#define CAM_NAME				"omap24xxcam" - -#define CAM_MCLK				96000000 - -/* number of bytes transferred per DMA request */ -#define DMA_THRESHOLD				32 - -/* - * NUM_CAMDMA_CHANNELS is the number of logical channels provided by - * the camera DMA controller. - */ -#define NUM_CAMDMA_CHANNELS			4 - -/* - * NUM_SG_DMA is the number of scatter-gather DMA transfers that can - * be queued. (We don't have any overlay sglists now.) - */ -#define NUM_SG_DMA				(VIDEO_MAX_FRAME) - -/* - * - * Register definitions. - * - */ - -/* subsystem register block offsets */ -#define CC_REG_OFFSET				0x00000400 -#define CAMDMA_REG_OFFSET			0x00000800 -#define CAMMMU_REG_OFFSET			0x00000C00 - -/* define camera subsystem register offsets */ -#define CAM_REVISION				0x000 -#define CAM_SYSCONFIG				0x010 -#define CAM_SYSSTATUS				0x014 -#define CAM_IRQSTATUS				0x018 -#define CAM_GPO					0x040 -#define CAM_GPI					0x050 - -/* define camera core register offsets */ -#define CC_REVISION				0x000 -#define CC_SYSCONFIG				0x010 -#define CC_SYSSTATUS				0x014 -#define CC_IRQSTATUS				0x018 -#define CC_IRQENABLE				0x01C -#define CC_CTRL					0x040 -#define CC_CTRL_DMA				0x044 -#define CC_CTRL_XCLK				0x048 -#define CC_FIFODATA				0x04C -#define CC_TEST					0x050 -#define CC_GENPAR				0x054 -#define CC_CCPFSCR				0x058 -#define CC_CCPFECR				0x05C -#define CC_CCPLSCR				0x060 -#define CC_CCPLECR				0x064 -#define CC_CCPDFR				0x068 - -/* define camera dma register offsets */ -#define CAMDMA_REVISION				0x000 -#define CAMDMA_IRQSTATUS_L0			0x008 -#define CAMDMA_IRQSTATUS_L1			0x00C -#define CAMDMA_IRQSTATUS_L2			0x010 -#define CAMDMA_IRQSTATUS_L3			0x014 -#define CAMDMA_IRQENABLE_L0			0x018 -#define CAMDMA_IRQENABLE_L1			0x01C -#define CAMDMA_IRQENABLE_L2			0x020 -#define CAMDMA_IRQENABLE_L3			0x024 -#define CAMDMA_SYSSTATUS			0x028 -#define CAMDMA_OCP_SYSCONFIG			0x02C -#define CAMDMA_CAPS_0				0x064 -#define CAMDMA_CAPS_2				0x06C -#define CAMDMA_CAPS_3				0x070 -#define CAMDMA_CAPS_4				0x074 -#define CAMDMA_GCR				0x078 -#define CAMDMA_CCR(n)				(0x080 + (n)*0x60) -#define CAMDMA_CLNK_CTRL(n)			(0x084 + (n)*0x60) -#define CAMDMA_CICR(n)				(0x088 + (n)*0x60) -#define CAMDMA_CSR(n)				(0x08C + (n)*0x60) -#define CAMDMA_CSDP(n)				(0x090 + (n)*0x60) -#define CAMDMA_CEN(n)				(0x094 + (n)*0x60) -#define CAMDMA_CFN(n)				(0x098 + (n)*0x60) -#define CAMDMA_CSSA(n)				(0x09C + (n)*0x60) -#define CAMDMA_CDSA(n)				(0x0A0 + (n)*0x60) -#define CAMDMA_CSEI(n)				(0x0A4 + (n)*0x60) -#define CAMDMA_CSFI(n)				(0x0A8 + (n)*0x60) -#define CAMDMA_CDEI(n)				(0x0AC + (n)*0x60) -#define CAMDMA_CDFI(n)				(0x0B0 + (n)*0x60) -#define CAMDMA_CSAC(n)				(0x0B4 + (n)*0x60) -#define CAMDMA_CDAC(n)				(0x0B8 + (n)*0x60) -#define CAMDMA_CCEN(n)				(0x0BC + (n)*0x60) -#define CAMDMA_CCFN(n)				(0x0C0 + (n)*0x60) -#define CAMDMA_COLOR(n)				(0x0C4 + (n)*0x60) - -/* define camera mmu register offsets */ -#define CAMMMU_REVISION				0x000 -#define CAMMMU_SYSCONFIG			0x010 -#define CAMMMU_SYSSTATUS			0x014 -#define CAMMMU_IRQSTATUS			0x018 -#define CAMMMU_IRQENABLE			0x01C -#define CAMMMU_WALKING_ST			0x040 -#define CAMMMU_CNTL				0x044 -#define CAMMMU_FAULT_AD				0x048 -#define CAMMMU_TTB				0x04C -#define CAMMMU_LOCK				0x050 -#define CAMMMU_LD_TLB				0x054 -#define CAMMMU_CAM				0x058 -#define CAMMMU_RAM				0x05C -#define CAMMMU_GFLUSH				0x060 -#define CAMMMU_FLUSH_ENTRY			0x064 -#define CAMMMU_READ_CAM				0x068 -#define CAMMMU_READ_RAM				0x06C -#define CAMMMU_EMU_FAULT_AD			0x070 - -/* Define bit fields within selected registers */ -#define CAM_REVISION_MAJOR			(15 << 4) -#define CAM_REVISION_MAJOR_SHIFT		4 -#define CAM_REVISION_MINOR			(15 << 0) -#define CAM_REVISION_MINOR_SHIFT		0 - -#define CAM_SYSCONFIG_SOFTRESET			(1 <<  1) -#define CAM_SYSCONFIG_AUTOIDLE			(1 <<  0) - -#define CAM_SYSSTATUS_RESETDONE			(1 <<  0) - -#define CAM_IRQSTATUS_CC_IRQ			(1 <<  4) -#define CAM_IRQSTATUS_MMU_IRQ			(1 <<  3) -#define CAM_IRQSTATUS_DMA_IRQ2			(1 <<  2) -#define CAM_IRQSTATUS_DMA_IRQ1			(1 <<  1) -#define CAM_IRQSTATUS_DMA_IRQ0			(1 <<  0) - -#define CAM_GPO_CAM_S_P_EN			(1 <<  1) -#define CAM_GPO_CAM_CCP_MODE			(1 <<  0) - -#define CAM_GPI_CC_DMA_REQ1			(1 << 24) -#define CAP_GPI_CC_DMA_REQ0			(1 << 23) -#define CAP_GPI_CAM_MSTANDBY			(1 << 21) -#define CAP_GPI_CAM_WAIT			(1 << 20) -#define CAP_GPI_CAM_S_DATA			(1 << 17) -#define CAP_GPI_CAM_S_CLK			(1 << 16) -#define CAP_GPI_CAM_P_DATA			(0xFFF << 3) -#define CAP_GPI_CAM_P_DATA_SHIFT		3 -#define CAP_GPI_CAM_P_VS			(1 <<  2) -#define CAP_GPI_CAM_P_HS			(1 <<  1) -#define CAP_GPI_CAM_P_CLK			(1 <<  0) - -#define CC_REVISION_MAJOR			(15 << 4) -#define CC_REVISION_MAJOR_SHIFT			4 -#define CC_REVISION_MINOR			(15 << 0) -#define CC_REVISION_MINOR_SHIFT			0 - -#define CC_SYSCONFIG_SIDLEMODE			(3 <<  3) -#define CC_SYSCONFIG_SIDLEMODE_FIDLE		(0 <<  3) -#define CC_SYSCONFIG_SIDLEMODE_NIDLE		(1 <<  3) -#define CC_SYSCONFIG_SOFTRESET			(1 <<  1) -#define CC_SYSCONFIG_AUTOIDLE			(1 <<  0) - -#define CC_SYSSTATUS_RESETDONE			(1 <<  0) - -#define CC_IRQSTATUS_FS_IRQ			(1 << 19) -#define CC_IRQSTATUS_LE_IRQ			(1 << 18) -#define CC_IRQSTATUS_LS_IRQ			(1 << 17) -#define CC_IRQSTATUS_FE_IRQ			(1 << 16) -#define CC_IRQSTATUS_FW_ERR_IRQ			(1 << 10) -#define CC_IRQSTATUS_FSC_ERR_IRQ		(1 <<  9) -#define CC_IRQSTATUS_SSC_ERR_IRQ		(1 <<  8) -#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ		(1 <<  4) -#define CC_IRQSTATUS_FIFO_FULL_IRQ		(1 <<  3) -#define CC_IRQSTATUS_FIFO_THR_IRQ		(1 <<  2) -#define CC_IRQSTATUS_FIFO_OF_IRQ		(1 <<  1) -#define CC_IRQSTATUS_FIFO_UF_IRQ		(1 <<  0) - -#define CC_IRQENABLE_FS_IRQ			(1 << 19) -#define CC_IRQENABLE_LE_IRQ			(1 << 18) -#define CC_IRQENABLE_LS_IRQ			(1 << 17) -#define CC_IRQENABLE_FE_IRQ			(1 << 16) -#define CC_IRQENABLE_FW_ERR_IRQ			(1 << 10) -#define CC_IRQENABLE_FSC_ERR_IRQ		(1 <<  9) -#define CC_IRQENABLE_SSC_ERR_IRQ		(1 <<  8) -#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ		(1 <<  4) -#define CC_IRQENABLE_FIFO_FULL_IRQ		(1 <<  3) -#define CC_IRQENABLE_FIFO_THR_IRQ		(1 <<  2) -#define CC_IRQENABLE_FIFO_OF_IRQ		(1 <<  1) -#define CC_IRQENABLE_FIFO_UF_IRQ		(1 <<  0) - -#define CC_CTRL_CC_ONE_SHOT			(1 << 20) -#define CC_CTRL_CC_IF_SYNCHRO			(1 << 19) -#define CC_CTRL_CC_RST				(1 << 18) -#define CC_CTRL_CC_FRAME_TRIG			(1 << 17) -#define CC_CTRL_CC_EN				(1 << 16) -#define CC_CTRL_NOBT_SYNCHRO			(1 << 13) -#define CC_CTRL_BT_CORRECT			(1 << 12) -#define CC_CTRL_PAR_ORDERCAM			(1 << 11) -#define CC_CTRL_PAR_CLK_POL			(1 << 10) -#define CC_CTRL_NOBT_HS_POL			(1 <<  9) -#define CC_CTRL_NOBT_VS_POL			(1 <<  8) -#define CC_CTRL_PAR_MODE			(7 <<  1) -#define CC_CTRL_PAR_MODE_SHIFT			1 -#define CC_CTRL_PAR_MODE_NOBT8			(0 <<  1) -#define CC_CTRL_PAR_MODE_NOBT10			(1 <<  1) -#define CC_CTRL_PAR_MODE_NOBT12			(2 <<  1) -#define CC_CTRL_PAR_MODE_BT8			(4 <<  1) -#define CC_CTRL_PAR_MODE_BT10			(5 <<  1) -#define CC_CTRL_PAR_MODE_FIFOTEST		(7 <<  1) -#define CC_CTRL_CCP_MODE			(1 <<  0) - -#define CC_CTRL_DMA_EN				(1 <<  8) -#define CC_CTRL_DMA_FIFO_THRESHOLD		(0x7F << 0) -#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT	0 - -#define CC_CTRL_XCLK_DIV			(0x1F << 0) -#define CC_CTRL_XCLK_DIV_SHIFT			0 -#define CC_CTRL_XCLK_DIV_STABLE_LOW		(0 <<  0) -#define CC_CTRL_XCLK_DIV_STABLE_HIGH		(1 <<  0) -#define CC_CTRL_XCLK_DIV_BYPASS			(31 << 0) - -#define CC_TEST_FIFO_RD_POINTER			(0xFF << 24) -#define CC_TEST_FIFO_RD_POINTER_SHIFT		24 -#define CC_TEST_FIFO_WR_POINTER			(0xFF << 16) -#define CC_TEST_FIFO_WR_POINTER_SHIFT		16 -#define CC_TEST_FIFO_LEVEL			(0xFF <<  8) -#define CC_TEST_FIFO_LEVEL_SHIFT		8 -#define CC_TEST_FIFO_LEVEL_PEAK			(0xFF <<  0) -#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT		0 - -#define CC_GENPAR_FIFO_DEPTH			(7 <<  0) -#define CC_GENPAR_FIFO_DEPTH_SHIFT		0 - -#define CC_CCPDFR_ALPHA				(0xFF <<  8) -#define CC_CCPDFR_ALPHA_SHIFT			8 -#define CC_CCPDFR_DATAFORMAT			(15 <<  0) -#define CC_CCPDFR_DATAFORMAT_SHIFT		0 -#define CC_CCPDFR_DATAFORMAT_YUV422BE		(0 <<  0) -#define CC_CCPDFR_DATAFORMAT_YUV422		(1 <<  0) -#define CC_CCPDFR_DATAFORMAT_YUV420		(2 <<  0) -#define CC_CCPDFR_DATAFORMAT_RGB444		(4 <<  0) -#define CC_CCPDFR_DATAFORMAT_RGB565		(5 <<  0) -#define CC_CCPDFR_DATAFORMAT_RGB888NDE		(6 <<  0) -#define CC_CCPDFR_DATAFORMAT_RGB888		(7 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW8NDE		(8 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW8		(9 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW10NDE		(10 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW10		(11 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW12NDE		(12 <<  0) -#define CC_CCPDFR_DATAFORMAT_RAW12		(13 <<  0) -#define CC_CCPDFR_DATAFORMAT_JPEG8		(15 <<  0) - -#define CAMDMA_REVISION_MAJOR			(15 << 4) -#define CAMDMA_REVISION_MAJOR_SHIFT		4 -#define CAMDMA_REVISION_MINOR			(15 << 0) -#define CAMDMA_REVISION_MINOR_SHIFT		0 - -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE		(3 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY	(0 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY	(1 << 12) -#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY	(2 << 12) -#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK		(1 <<  9) -#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK		(1 <<  8) -#define CAMDMA_OCP_SYSCONFIG_EMUFREE		(1 <<  5) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE		(3 <<  3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE	(0 <<  3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE	(1 <<  3) -#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE	(2 <<  3) -#define CAMDMA_OCP_SYSCONFIG_SOFTRESET		(1 <<  1) -#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE		(1 <<  0) - -#define CAMDMA_SYSSTATUS_RESETDONE		(1 <<  0) - -#define CAMDMA_GCR_ARBITRATION_RATE		(0xFF << 16) -#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT	16 -#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH	(0xFF << 0) -#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT	0 - -#define CAMDMA_CCR_SEL_SRC_DST_SYNC		(1 << 24) -#define CAMDMA_CCR_PREFETCH			(1 << 23) -#define CAMDMA_CCR_SUPERVISOR			(1 << 22) -#define CAMDMA_CCR_SECURE			(1 << 21) -#define CAMDMA_CCR_BS				(1 << 18) -#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE	(1 << 17) -#define CAMDMA_CCR_CONSTANT_FILL_ENABLE		(1 << 16) -#define CAMDMA_CCR_DST_AMODE			(3 << 14) -#define CAMDMA_CCR_DST_AMODE_CONST_ADDR		(0 << 14) -#define CAMDMA_CCR_DST_AMODE_POST_INC		(1 << 14) -#define CAMDMA_CCR_DST_AMODE_SGL_IDX		(2 << 14) -#define CAMDMA_CCR_DST_AMODE_DBL_IDX		(3 << 14) -#define CAMDMA_CCR_SRC_AMODE			(3 << 12) -#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR		(0 << 12) -#define CAMDMA_CCR_SRC_AMODE_POST_INC		(1 << 12) -#define CAMDMA_CCR_SRC_AMODE_SGL_IDX		(2 << 12) -#define CAMDMA_CCR_SRC_AMODE_DBL_IDX		(3 << 12) -#define CAMDMA_CCR_WR_ACTIVE			(1 << 10) -#define CAMDMA_CCR_RD_ACTIVE			(1 <<  9) -#define CAMDMA_CCR_SUSPEND_SENSITIVE		(1 <<  8) -#define CAMDMA_CCR_ENABLE			(1 <<  7) -#define CAMDMA_CCR_PRIO				(1 <<  6) -#define CAMDMA_CCR_FS				(1 <<  5) -#define CAMDMA_CCR_SYNCHRO			((3 << 19) | (31 << 0)) -#define CAMDMA_CCR_SYNCHRO_CAMERA		0x01 - -#define CAMDMA_CLNK_CTRL_ENABLE_LNK		(1 << 15) -#define CAMDMA_CLNK_CTRL_NEXTLCH_ID		(0x1F << 0) -#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT	0 - -#define CAMDMA_CICR_MISALIGNED_ERR_IE		(1 << 11) -#define CAMDMA_CICR_SUPERVISOR_ERR_IE		(1 << 10) -#define CAMDMA_CICR_SECURE_ERR_IE		(1 <<  9) -#define CAMDMA_CICR_TRANS_ERR_IE		(1 <<  8) -#define CAMDMA_CICR_PACKET_IE			(1 <<  7) -#define CAMDMA_CICR_BLOCK_IE			(1 <<  5) -#define CAMDMA_CICR_LAST_IE			(1 <<  4) -#define CAMDMA_CICR_FRAME_IE			(1 <<  3) -#define CAMDMA_CICR_HALF_IE			(1 <<  2) -#define CAMDMA_CICR_DROP_IE			(1 <<  1) - -#define CAMDMA_CSR_MISALIGNED_ERR		(1 << 11) -#define CAMDMA_CSR_SUPERVISOR_ERR		(1 << 10) -#define CAMDMA_CSR_SECURE_ERR			(1 <<  9) -#define CAMDMA_CSR_TRANS_ERR			(1 <<  8) -#define CAMDMA_CSR_PACKET			(1 <<  7) -#define CAMDMA_CSR_SYNC				(1 <<  6) -#define CAMDMA_CSR_BLOCK			(1 <<  5) -#define CAMDMA_CSR_LAST				(1 <<  4) -#define CAMDMA_CSR_FRAME			(1 <<  3) -#define CAMDMA_CSR_HALF				(1 <<  2) -#define CAMDMA_CSR_DROP				(1 <<  1) - -#define CAMDMA_CSDP_SRC_ENDIANNESS		(1 << 21) -#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK		(1 << 20) -#define CAMDMA_CSDP_DST_ENDIANNESS		(1 << 19) -#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK		(1 << 18) -#define CAMDMA_CSDP_WRITE_MODE			(3 << 16) -#define CAMDMA_CSDP_WRITE_MODE_WRNP		(0 << 16) -#define CAMDMA_CSDP_WRITE_MODE_POSTED		(1 << 16) -#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP	(2 << 16) -#define CAMDMA_CSDP_DST_BURST_EN		(3 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_1		(0 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_16		(1 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_32		(2 << 14) -#define CAMDMA_CSDP_DST_BURST_EN_64		(3 << 14) -#define CAMDMA_CSDP_DST_PACKED			(1 << 13) -#define CAMDMA_CSDP_WR_ADD_TRSLT		(15 << 9) -#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD	(3 <<  9) -#define CAMDMA_CSDP_SRC_BURST_EN		(3 <<  7) -#define CAMDMA_CSDP_SRC_BURST_EN_1		(0 <<  7) -#define CAMDMA_CSDP_SRC_BURST_EN_16		(1 <<  7) -#define CAMDMA_CSDP_SRC_BURST_EN_32		(2 <<  7) -#define CAMDMA_CSDP_SRC_BURST_EN_64		(3 <<  7) -#define CAMDMA_CSDP_SRC_PACKED			(1 <<  6) -#define CAMDMA_CSDP_RD_ADD_TRSLT		(15 << 2) -#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD	(3 <<  2) -#define CAMDMA_CSDP_DATA_TYPE			(3 <<  0) -#define CAMDMA_CSDP_DATA_TYPE_8BITS		(0 <<  0) -#define CAMDMA_CSDP_DATA_TYPE_16BITS		(1 <<  0) -#define CAMDMA_CSDP_DATA_TYPE_32BITS		(2 <<  0) - -#define CAMMMU_SYSCONFIG_AUTOIDLE		(1 <<  0) - -/* - * - * Declarations. - * - */ - -/* forward declarations */ -struct omap24xxcam_sgdma; -struct omap24xxcam_dma; - -typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam, -				 u32 status, void *arg); -typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam, -			       u32 status, void *arg); - -struct channel_state { -	dma_callback_t callback; -	void *arg; -}; - -/* sgdma state for each of the possible videobuf_buffers + 2 overlays */ -struct sgdma_state { -	const struct scatterlist *sglist; -	int sglen;		 /* number of sglist entries */ -	int next_sglist;	 /* index of next sglist entry to process */ -	unsigned int bytes_read; /* number of bytes read */ -	unsigned int len;        /* total length of sglist (excluding -				  * bytes due to page alignment) */ -	int queued_sglist;	 /* number of sglist entries queued for DMA */ -	u32 csr;		 /* DMA return code */ -	sgdma_callback_t callback; -	void *arg; -}; - -/* physical DMA channel management */ -struct omap24xxcam_dma { -	spinlock_t lock;	/* Lock for the whole structure. */ - -	void __iomem *base;	/* base address for dma controller */ - -	/* While dma_stop!=0, an attempt to start a new DMA transfer will -	 * fail. -	 */ -	atomic_t dma_stop; -	int free_dmach;		/* number of dma channels free */ -	int next_dmach;		/* index of next dma channel to use */ -	struct channel_state ch_state[NUM_CAMDMA_CHANNELS]; -}; - -/* scatter-gather DMA (scatterlist stuff) management */ -struct omap24xxcam_sgdma { -	struct omap24xxcam_dma dma; - -	spinlock_t lock;	/* Lock for the fields below. */ -	int free_sgdma;		/* number of free sg dma slots */ -	int next_sgdma;		/* index of next sg dma slot to use */ -	struct sgdma_state sg_state[NUM_SG_DMA]; - -	/* Reset timer data */ -	struct timer_list reset_timer; -}; - -/* per-device data structure */ -struct omap24xxcam_device { -	/*** mutex  ***/ -	/* -	 * mutex serialises access to this structure. Also camera -	 * opening and releasing is synchronised by this. -	 */ -	struct mutex mutex; - -	struct v4l2_device v4l2_dev; - -	/*** general driver state information ***/ -	atomic_t users; -	/* -	 * Lock to serialise core enabling and disabling and access to -	 * sgdma_in_queue. -	 */ -	spinlock_t core_enable_disable_lock; -	/* -	 * Number or sgdma requests in scatter-gather queue, protected -	 * by the lock above. -	 */ -	int sgdma_in_queue; -	/* -	 * Sensor interface parameters: interface type, CC_CTRL -	 * register value and interface specific data. -	 */ -	int if_type; -	union { -		struct parallel { -			u32 xclk; -		} bt656; -	} if_u; -	u32 cc_ctrl; - -	/*** subsystem structures ***/ -	struct omap24xxcam_sgdma sgdma; - -	/*** hardware resources ***/ -	unsigned int irq; -	void __iomem *mmio_base; -	unsigned long mmio_base_phys; -	unsigned long mmio_size; - -	/*** interfaces and device ***/ -	struct v4l2_int_device *sdev; -	struct device *dev; -	struct video_device *vfd; - -	/*** camera and sensor reset related stuff ***/ -	struct work_struct sensor_reset_work; -	/* -	 * We're in the middle of a reset. Don't enable core if this -	 * is non-zero! This exists to help decisionmaking in a case -	 * where videobuf_qbuf is called while we are in the middle of -	 * a reset. -	 */ -	atomic_t in_reset; -	/* -	 * Non-zero if we don't want any resets for now. Used to -	 * prevent reset work to run when we're about to stop -	 * streaming. -	 */ -	atomic_t reset_disable; - -	/*** video device parameters ***/ -	int capture_mem; - -	/*** camera module clocks ***/ -	struct clk *fck; -	struct clk *ick; - -	/*** capture data ***/ -	/* file handle, if streaming is on */ -	struct file *streaming; -}; - -/* Per-file handle data. */ -struct omap24xxcam_fh { -	spinlock_t vbq_lock; /* spinlock for the videobuf queue */ -	struct videobuf_queue vbq; -	struct v4l2_pix_format pix; /* serialise pix by vbq->lock */ -	atomic_t field_count; /* field counter for videobuf_buffer */ -	/* accessing cam here doesn't need serialisation: it's constant */ -	struct omap24xxcam_device *cam; -}; - -/* - * - * Register I/O functions. - * - */ - -static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset) -{ -	return readl(base + offset); -} - -static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset, -					  u32 val) -{ -	writel(val, base + offset); -	return val; -} - -static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset, -					    u32 val, u32 mask) -{ -	u32 __iomem *addr = base + offset; -	u32 new_val = (readl(addr) & ~mask) | (val & mask); - -	writel(new_val, addr); -	return new_val; -} - -/* - * - * Function prototypes. - * - */ - -/* dma prototypes */ - -void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma); -void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma); - -/* sgdma prototypes */ - -void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma); -int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, -			    const struct scatterlist *sglist, int sglen, -			    int len, sgdma_callback_t callback, void *arg); -void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); -void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, -			    void __iomem *base, -			    void (*reset_callback)(unsigned long data), -			    unsigned long reset_callback_data); -void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); - -#endif 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); diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 40b298ab87f..deba425e3d8 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -435,10 +435,10 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)  	return 0;  } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq)  {  	struct camif_vp *vp = vb2_get_drv_priv(vq); -	return camif_stop_capture(vp); +	camif_stop_capture(vp);  }  static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, @@ -1160,7 +1160,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx)  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct camif_buffer);  	q->drv_priv = vp; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	ret = vb2_queue_init(q);  	if (ret) @@ -1592,26 +1592,27 @@ int s3c_camif_create_subdev(struct camif_dev *camif)  			ARRAY_SIZE(s3c_camif_test_pattern_menu) - 1, 0, 0,  			s3c_camif_test_pattern_menu); -	camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler, +	if (camif->variant->has_img_effect) { +		camif->ctrl_colorfx = v4l2_ctrl_new_std_menu(handler,  				&s3c_camif_subdev_ctrl_ops,  				V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,  				~0x981f, V4L2_COLORFX_NONE); -	camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler, +		camif->ctrl_colorfx_cbcr = v4l2_ctrl_new_std(handler,  				&s3c_camif_subdev_ctrl_ops,  				V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); +	} +  	if (handler->error) {  		v4l2_ctrl_handler_free(handler);  		media_entity_cleanup(&sd->entity);  		return handler->error;  	} -	v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx, +	if (camif->variant->has_img_effect) +		v4l2_ctrl_auto_cluster(2, &camif->ctrl_colorfx,  			       V4L2_COLORFX_SET_CBCR, false); -	if (!camif->variant->has_img_effect) { -		camif->ctrl_colorfx->flags |= V4L2_CTRL_FLAG_DISABLED; -		camif->ctrl_colorfx_cbcr->flags |= V4L2_CTRL_FLAG_DISABLED; -	} +  	sd->ctrl_handler = handler;  	v4l2_set_subdevdata(sd, camif); diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index fd6289d60cd..357af1ebaed 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -136,10 +136,9 @@ static int g2d_buf_prepare(struct vb2_buffer *vb)  static void g2d_buf_queue(struct vb2_buffer *vb)  {  	struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); -	v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);  } -  static struct vb2_ops g2d_qops = {  	.queue_setup	= g2d_queue_setup,  	.buf_prepare	= g2d_buf_prepare, @@ -158,7 +157,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->ops = &g2d_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops;  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->lock = &ctx->dev->mutex;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -170,7 +170,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->ops = &g2d_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops;  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->lock = &ctx->dev->mutex;  	return vb2_queue_init(dst_vq);  } @@ -253,9 +254,9 @@ static int g2d_open(struct file *file)  		kfree(ctx);  		return -ERESTARTSYS;  	} -	ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); -	if (IS_ERR(ctx->m2m_ctx)) { -		ret = PTR_ERR(ctx->m2m_ctx); +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); +	if (IS_ERR(ctx->fh.m2m_ctx)) { +		ret = PTR_ERR(ctx->fh.m2m_ctx);  		mutex_unlock(&dev->mutex);  		kfree(ctx);  		return ret; @@ -324,7 +325,7 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)  	struct vb2_queue *vq;  	struct g2d_frame *frm; -	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);  	if (!vq)  		return -EINVAL;  	frm = get_frame(ctx, f->type); @@ -384,7 +385,7 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)  	ret = vidioc_try_fmt(file, prv, f);  	if (ret)  		return ret; -	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);  	if (vb2_is_busy(vq)) {  		v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);  		return -EBUSY; @@ -410,72 +411,6 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)  	return 0;  } -static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait) -{ -	struct g2d_ctx *ctx = fh2ctx(file->private_data); -	struct g2d_dev *dev = ctx->dev; -	unsigned int res; - -	mutex_lock(&dev->mutex); -	res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -	mutex_unlock(&dev->mutex); -	return res; -} - -static int g2d_mmap(struct file *file, struct vm_area_struct *vma) -{ -	struct g2d_ctx *ctx = fh2ctx(file->private_data); -	struct g2d_dev *dev = ctx->dev; -	int ret; - -	if (mutex_lock_interruptible(&dev->mutex)) -		return -ERESTARTSYS; -	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -	mutex_unlock(&dev->mutex); -	return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, -			struct v4l2_requestbuffers *reqbufs) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int vidioc_querybuf(struct file *file, void *priv, -			struct v4l2_buffer *buf) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - - -static int vidioc_streamon(struct file *file, void *priv, -					enum v4l2_buf_type type) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int vidioc_streamoff(struct file *file, void *priv, -					enum v4l2_buf_type type) -{ -	struct g2d_ctx *ctx = priv; -	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} -  static int vidioc_cropcap(struct file *file, void *priv,  					struct v4l2_cropcap *cr)  { @@ -551,20 +486,6 @@ static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *c  	return 0;  } -static void g2d_lock(void *prv) -{ -	struct g2d_ctx *ctx = prv; -	struct g2d_dev *dev = ctx->dev; -	mutex_lock(&dev->mutex); -} - -static void g2d_unlock(void *prv) -{ -	struct g2d_ctx *ctx = prv; -	struct g2d_dev *dev = ctx->dev; -	mutex_unlock(&dev->mutex); -} -  static void job_abort(void *prv)  {  	struct g2d_ctx *ctx = prv; @@ -589,8 +510,8 @@ static void device_run(void *prv)  	dev->curr = ctx; -	src = v4l2_m2m_next_src_buf(ctx->m2m_ctx); -	dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); +	src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); +	dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);  	clk_enable(dev->gate);  	g2d_reset(dev); @@ -631,18 +552,21 @@ static irqreturn_t g2d_isr(int irq, void *prv)  	BUG_ON(ctx == NULL); -	src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); -	dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); +	src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); +	dst = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);  	BUG_ON(src == NULL);  	BUG_ON(dst == NULL);  	dst->v4l2_buf.timecode = src->v4l2_buf.timecode;  	dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp; +	dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst->v4l2_buf.flags |= +		src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  	v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);  	v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); -	v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); +	v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx);  	dev->curr = NULL;  	wake_up(&dev->irq_queue); @@ -653,9 +577,9 @@ static const struct v4l2_file_operations g2d_fops = {  	.owner		= THIS_MODULE,  	.open		= g2d_open,  	.release	= g2d_release, -	.poll		= g2d_poll, +	.poll		= v4l2_m2m_fop_poll,  	.unlocked_ioctl	= video_ioctl2, -	.mmap		= g2d_mmap, +	.mmap		= v4l2_m2m_fop_mmap,  };  static const struct v4l2_ioctl_ops g2d_ioctl_ops = { @@ -671,14 +595,13 @@ static const struct v4l2_ioctl_ops g2d_ioctl_ops = {  	.vidioc_try_fmt_vid_out		= vidioc_try_fmt,  	.vidioc_s_fmt_vid_out		= vidioc_s_fmt, -	.vidioc_reqbufs			= vidioc_reqbufs, -	.vidioc_querybuf		= vidioc_querybuf, - -	.vidioc_qbuf			= vidioc_qbuf, -	.vidioc_dqbuf			= vidioc_dqbuf, +	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs, +	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf, +	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf, +	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf, -	.vidioc_streamon		= vidioc_streamon, -	.vidioc_streamoff		= vidioc_streamoff, +	.vidioc_streamon		= v4l2_m2m_ioctl_streamon, +	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,  	.vidioc_g_crop			= vidioc_g_crop,  	.vidioc_s_crop			= vidioc_s_crop, @@ -697,8 +620,6 @@ static struct video_device g2d_videodev = {  static struct v4l2_m2m_ops g2d_m2m_ops = {  	.device_run	= device_run,  	.job_abort	= job_abort, -	.lock		= g2d_lock, -	.unlock		= g2d_unlock,  };  static const struct of_device_id exynos_g2d_match[]; @@ -840,7 +761,7 @@ put_clk:  static int g2d_remove(struct platform_device *pdev)  { -	struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev); +	struct g2d_dev *dev = platform_get_drvdata(pdev);  	v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);  	v4l2_m2m_release(dev->m2m_dev); diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index 300ca05ba40..b0e52ab7ecd 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -57,7 +57,6 @@ struct g2d_frame {  struct g2d_ctx {  	struct v4l2_fh fh;  	struct g2d_dev		*dev; -	struct v4l2_m2m_ctx	*m2m_ctx;  	struct g2d_frame	in;  	struct g2d_frame	out;  	struct v4l2_ctrl	*ctrl_hflip; diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile index d18cb5edd2d..a1a9169254c 100644 --- a/drivers/media/platform/s5p-jpeg/Makefile +++ b/drivers/media/platform/s5p-jpeg/Makefile @@ -1,2 +1,2 @@ -s5p-jpeg-objs := jpeg-core.o +s5p-jpeg-objs := jpeg-core.o jpeg-hw-exynos4.o jpeg-hw-s5p.o  obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg.o diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 15d23968d1d..0dcb796ecad 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1,9 +1,10 @@  /* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c   * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd.   *		http://www.samsung.com   *   * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Author: Jacek Anaszewski <j.anaszewski@samsung.com>   *   * 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 @@ -17,6 +18,7 @@  #include <linux/io.h>  #include <linux/kernel.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/pm_runtime.h>  #include <linux/slab.h> @@ -28,70 +30,233 @@  #include <media/videobuf2-dma-contig.h>  #include "jpeg-core.h" -#include "jpeg-hw.h" +#include "jpeg-hw-s5p.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-regs.h" -static struct s5p_jpeg_fmt formats_enc[] = { +static struct s5p_jpeg_fmt sjpeg_formats[] = {  	{  		.name		= "JPEG JFIF",  		.fourcc		= V4L2_PIX_FMT_JPEG, +		.flags		= SJPEG_FMT_FLAG_ENC_CAPTURE | +				  SJPEG_FMT_FLAG_DEC_OUTPUT | +				  SJPEG_FMT_FLAG_S5P | +				  SJPEG_FMT_FLAG_EXYNOS4, +	}, +	{ +		.name		= "YUV 4:2:2 packed, YCbYCr", +		.fourcc		= V4L2_PIX_FMT_YUYV, +		.depth		= 16,  		.colplanes	= 1, -		.types		= MEM2MEM_CAPTURE, +		.h_align	= 4, +		.v_align	= 3, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_S5P | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_422,  	},  	{  		.name		= "YUV 4:2:2 packed, YCbYCr",  		.fourcc		= V4L2_PIX_FMT_YUYV,  		.depth		= 16,  		.colplanes	= 1, -		.types		= MEM2MEM_OUTPUT, +		.h_align	= 1, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_422, +	}, +	{ +		.name		= "YUV 4:2:2 packed, YCrYCb", +		.fourcc		= V4L2_PIX_FMT_YVYU, +		.depth		= 16, +		.colplanes	= 1, +		.h_align	= 1, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_422,  	},  	{  		.name		= "RGB565",  		.fourcc		= V4L2_PIX_FMT_RGB565,  		.depth		= 16,  		.colplanes	= 1, -		.types		= MEM2MEM_OUTPUT, +		.h_align	= 0, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_444,  	}, -}; -#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc) - -static struct s5p_jpeg_fmt formats_dec[] = {  	{ -		.name		= "YUV 4:2:0 planar, YCbCr", -		.fourcc		= V4L2_PIX_FMT_YUV420, +		.name		= "RGB565", +		.fourcc		= V4L2_PIX_FMT_RGB565, +		.depth		= 16, +		.colplanes	= 1, +		.h_align	= 0, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_S5P | +				  SJPEG_FMT_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_444, +	}, +	{ +		.name		= "ARGB8888, 32 bpp", +		.fourcc		= V4L2_PIX_FMT_RGB32, +		.depth		= 32, +		.colplanes	= 1, +		.h_align	= 0, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_444, +	}, +	{ +		.name		= "YUV 4:4:4 planar, Y/CbCr", +		.fourcc		= V4L2_PIX_FMT_NV24, +		.depth		= 24, +		.colplanes	= 2, +		.h_align	= 0, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_444, +	}, +	{ +		.name		= "YUV 4:4:4 planar, Y/CrCb", +		.fourcc		= V4L2_PIX_FMT_NV42, +		.depth		= 24, +		.colplanes	= 2, +		.h_align	= 0, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_444, +	}, +	{ +		.name		= "YUV 4:2:2 planar, Y/CrCb", +		.fourcc		= V4L2_PIX_FMT_NV61, +		.depth		= 16, +		.colplanes	= 2, +		.h_align	= 1, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_422, +	}, +	{ +		.name		= "YUV 4:2:2 planar, Y/CbCr", +		.fourcc		= V4L2_PIX_FMT_NV16, +		.depth		= 16, +		.colplanes	= 2, +		.h_align	= 1, +		.v_align	= 0, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_422, +	}, +	{ +		.name		= "YUV 4:2:0 planar, Y/CbCr", +		.fourcc		= V4L2_PIX_FMT_NV12,  		.depth		= 12, -		.colplanes	= 3, +		.colplanes	= 2, +		.h_align	= 1, +		.v_align	= 1, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_420, +	}, +	{ +		.name		= "YUV 4:2:0 planar, Y/CbCr", +		.fourcc		= V4L2_PIX_FMT_NV12, +		.depth		= 12, +		.colplanes	= 2,  		.h_align	= 4,  		.v_align	= 4, -		.types		= MEM2MEM_CAPTURE, +		.flags		= SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_S5P | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_420,  	},  	{ -		.name		= "YUV 4:2:2 packed, YCbYCr", -		.fourcc		= V4L2_PIX_FMT_YUYV, -		.depth		= 16, -		.colplanes	= 1, -		.h_align	= 4, -		.v_align	= 3, -		.types		= MEM2MEM_CAPTURE, +		.name		= "YUV 4:2:0 planar, Y/CrCb", +		.fourcc		= V4L2_PIX_FMT_NV21, +		.depth		= 12, +		.colplanes	= 2, +		.h_align	= 1, +		.v_align	= 1, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_420,  	},  	{ -		.name		= "JPEG JFIF", -		.fourcc		= V4L2_PIX_FMT_JPEG, +		.name		= "YUV 4:2:0 contiguous 3-planar, Y/Cb/Cr", +		.fourcc		= V4L2_PIX_FMT_YUV420, +		.depth		= 12, +		.colplanes	= 3, +		.h_align	= 1, +		.v_align	= 1, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_420, +	}, +	{ +		.name		= "Gray", +		.fourcc		= V4L2_PIX_FMT_GREY, +		.depth		= 8,  		.colplanes	= 1, -		.types		= MEM2MEM_OUTPUT, +		.flags		= SJPEG_FMT_FLAG_ENC_OUTPUT | +				  SJPEG_FMT_FLAG_DEC_CAPTURE | +				  SJPEG_FMT_FLAG_EXYNOS4 | +				  SJPEG_FMT_NON_RGB, +		.subsampling	= V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY,  	},  }; -#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec) +#define SJPEG_NUM_FORMATS ARRAY_SIZE(sjpeg_formats)  static const unsigned char qtbl_luminance[4][64] = { -	{/* level 1 - high quality */ -		 8,  6,  6,  8, 12, 14, 16, 17, -		 6,  6,  6,  8, 10, 13, 12, 15, -		 6,  6,  7,  8, 13, 14, 18, 24, -		 8,  8,  8, 14, 13, 19, 24, 35, -		12, 10, 13, 13, 20, 26, 34, 39, -		14, 13, 14, 19, 26, 34, 39, 39, -		16, 12, 18, 24, 34, 39, 39, 39, -		17, 15, 24, 35, 39, 39, 39, 39 +	{/*level 0 - high compression quality */ +		20, 16, 25, 39, 50, 46, 62, 68, +		16, 18, 23, 38, 38, 53, 65, 68, +		25, 23, 31, 38, 53, 65, 68, 68, +		39, 38, 38, 53, 65, 68, 68, 68, +		50, 38, 53, 65, 68, 68, 68, 68, +		46, 53, 65, 68, 68, 68, 68, 68, +		62, 65, 68, 68, 68, 68, 68, 68, +		68, 68, 68, 68, 68, 68, 68, 68 +	}, +	{/* level 1 */ +		16, 11, 11, 16, 23, 27, 31, 30, +		11, 12, 12, 15, 20, 23, 23, 30, +		11, 12, 13, 16, 23, 26, 35, 47, +		16, 15, 16, 23, 26, 37, 47, 64, +		23, 20, 23, 26, 39, 51, 64, 64, +		27, 23, 26, 37, 51, 64, 64, 64, +		31, 23, 35, 47, 64, 64, 64, 64, +		30, 30, 47, 64, 64, 64, 64, 64  	},  	{/* level 2 */  		12,  8,  8, 12, 17, 21, 24, 23, @@ -103,38 +268,38 @@ static const unsigned char qtbl_luminance[4][64] = {  		24, 18, 27, 36, 51, 59, 59, 59,  		23, 23, 36, 53, 59, 59, 59, 59  	}, -	{/* level 3 */ -		16, 11, 11, 16, 23, 27, 31, 30, -		11, 12, 12, 15, 20, 23, 23, 30, -		11, 12, 13, 16, 23, 26, 35, 47, -		16, 15, 16, 23, 26, 37, 47, 64, -		23, 20, 23, 26, 39, 51, 64, 64, -		27, 23, 26, 37, 51, 64, 64, 64, -		31, 23, 35, 47, 64, 64, 64, 64, -		30, 30, 47, 64, 64, 64, 64, 64 -	}, -	{/*level 4 - low quality */ -		20, 16, 25, 39, 50, 46, 62, 68, -		16, 18, 23, 38, 38, 53, 65, 68, -		25, 23, 31, 38, 53, 65, 68, 68, -		39, 38, 38, 53, 65, 68, 68, 68, -		50, 38, 53, 65, 68, 68, 68, 68, -		46, 53, 65, 68, 68, 68, 68, 68, -		62, 65, 68, 68, 68, 68, 68, 68, -		68, 68, 68, 68, 68, 68, 68, 68 +	{/* level 3 - low compression quality */ +		 8,  6,  6,  8, 12, 14, 16, 17, +		 6,  6,  6,  8, 10, 13, 12, 15, +		 6,  6,  7,  8, 13, 14, 18, 24, +		 8,  8,  8, 14, 13, 19, 24, 35, +		12, 10, 13, 13, 20, 26, 34, 39, +		14, 13, 14, 19, 26, 34, 39, 39, +		16, 12, 18, 24, 34, 39, 39, 39, +		17, 15, 24, 35, 39, 39, 39, 39  	}  };  static const unsigned char qtbl_chrominance[4][64] = { -	{/* level 1 - high quality */ -		 9,  8,  9, 11, 14, 17, 19, 24, -		 8, 10,  9, 11, 14, 13, 17, 22, -		 9,  9, 13, 14, 13, 15, 23, 26, -		11, 11, 14, 14, 15, 20, 26, 33, -		14, 14, 13, 15, 20, 24, 33, 39, -		17, 13, 15, 20, 24, 32, 39, 39, -		19, 17, 23, 26, 33, 39, 39, 39, -		24, 22, 26, 33, 39, 39, 39, 39 +	{/*level 0 - high compression quality */ +		21, 25, 32, 38, 54, 68, 68, 68, +		25, 28, 24, 38, 54, 68, 68, 68, +		32, 24, 32, 43, 66, 68, 68, 68, +		38, 38, 43, 53, 68, 68, 68, 68, +		54, 54, 66, 68, 68, 68, 68, 68, +		68, 68, 68, 68, 68, 68, 68, 68, +		68, 68, 68, 68, 68, 68, 68, 68, +		68, 68, 68, 68, 68, 68, 68, 68 +	}, +	{/* level 1 */ +		17, 15, 17, 21, 20, 26, 38, 48, +		15, 19, 18, 17, 20, 26, 35, 43, +		17, 18, 20, 22, 26, 30, 46, 53, +		21, 17, 22, 28, 30, 39, 53, 64, +		20, 20, 26, 30, 39, 48, 64, 64, +		26, 26, 30, 39, 48, 63, 64, 64, +		38, 35, 46, 53, 64, 64, 64, 64, +		48, 43, 53, 64, 64, 64, 64, 64  	},  	{/* level 2 */  		13, 11, 13, 16, 20, 20, 29, 37, @@ -146,25 +311,15 @@ static const unsigned char qtbl_chrominance[4][64] = {  		29, 26, 35, 40, 50, 59, 59, 59,  		37, 32, 40, 50, 59, 59, 59, 59  	}, -	{/* level 3 */ -		17, 15, 17, 21, 20, 26, 38, 48, -		15, 19, 18, 17, 20, 26, 35, 43, -		17, 18, 20, 22, 26, 30, 46, 53, -		21, 17, 22, 28, 30, 39, 53, 64, -		20, 20, 26, 30, 39, 48, 64, 64, -		26, 26, 30, 39, 48, 63, 64, 64, -		38, 35, 46, 53, 64, 64, 64, 64, -		48, 43, 53, 64, 64, 64, 64, 64 -	}, -	{/*level 4 - low quality */ -		21, 25, 32, 38, 54, 68, 68, 68, -		25, 28, 24, 38, 54, 68, 68, 68, -		32, 24, 32, 43, 66, 68, 68, 68, -		38, 38, 43, 53, 68, 68, 68, 68, -		54, 54, 66, 68, 68, 68, 68, 68, -		68, 68, 68, 68, 68, 68, 68, 68, -		68, 68, 68, 68, 68, 68, 68, 68, -		68, 68, 68, 68, 68, 68, 68, 68 +	{/* level 3 - low compression quality */ +		 9,  8,  9, 11, 14, 17, 19, 24, +		 8, 10,  9, 11, 14, 13, 17, 22, +		 9,  9, 13, 14, 13, 15, 23, 26, +		11, 11, 14, 14, 15, 20, 26, 33, +		14, 14, 13, 15, 20, 24, 33, 39, +		17, 13, 15, 20, 24, 32, 39, 39, +		19, 17, 23, 26, 33, 39, 39, 39, +		24, 22, 26, 33, 39, 39, 39, 39  	}  }; @@ -202,6 +357,106 @@ static const unsigned char hactblg0[162] = {  	0xf9, 0xfa  }; +/* + * Fourcc downgrade schema lookup tables for 422 and 420 + * chroma subsampling - fourcc on each position maps on the + * fourcc from the table fourcc_to_dwngrd_schema_id which allows + * to get the most suitable fourcc counterpart for the given + * downgraded subsampling property. + */ +static const u32 subs422_fourcc_dwngrd_schema[] = { +	V4L2_PIX_FMT_NV16, +	V4L2_PIX_FMT_NV61, +}; + +static const u32 subs420_fourcc_dwngrd_schema[] = { +	V4L2_PIX_FMT_NV12, +	V4L2_PIX_FMT_NV21, +	V4L2_PIX_FMT_NV12, +	V4L2_PIX_FMT_NV21, +	V4L2_PIX_FMT_NV12, +	V4L2_PIX_FMT_NV21, +	V4L2_PIX_FMT_GREY, +	V4L2_PIX_FMT_GREY, +	V4L2_PIX_FMT_GREY, +	V4L2_PIX_FMT_GREY, +}; + +/* + * Lookup table for translation of a fourcc to the position + * of its downgraded counterpart in the *fourcc_dwngrd_schema + * tables. + */ +static const u32 fourcc_to_dwngrd_schema_id[] = { +	V4L2_PIX_FMT_NV24, +	V4L2_PIX_FMT_NV42, +	V4L2_PIX_FMT_NV16, +	V4L2_PIX_FMT_NV61, +	V4L2_PIX_FMT_YUYV, +	V4L2_PIX_FMT_YVYU, +	V4L2_PIX_FMT_NV12, +	V4L2_PIX_FMT_NV21, +	V4L2_PIX_FMT_YUV420, +	V4L2_PIX_FMT_GREY, +}; + +static int s5p_jpeg_get_dwngrd_sch_id_by_fourcc(u32 fourcc) +{ +	int i; +	for (i = 0; i < ARRAY_SIZE(fourcc_to_dwngrd_schema_id); ++i) { +		if (fourcc_to_dwngrd_schema_id[i] == fourcc) +			return i; +	} + +	return -EINVAL; +} + +static int s5p_jpeg_adjust_fourcc_to_subsampling( +					enum v4l2_jpeg_chroma_subsampling subs, +					u32 in_fourcc, +					u32 *out_fourcc, +					struct s5p_jpeg_ctx *ctx) +{ +	int dwngrd_sch_id; + +	if (ctx->subsampling != V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { +		dwngrd_sch_id = +			s5p_jpeg_get_dwngrd_sch_id_by_fourcc(in_fourcc); +		if (dwngrd_sch_id < 0) +			return -EINVAL; +	} + +	switch (ctx->subsampling) { +	case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: +		*out_fourcc = V4L2_PIX_FMT_GREY; +		break; +	case V4L2_JPEG_CHROMA_SUBSAMPLING_420: +		if (dwngrd_sch_id > +				ARRAY_SIZE(subs420_fourcc_dwngrd_schema) - 1) +			return -EINVAL; +		*out_fourcc = subs420_fourcc_dwngrd_schema[dwngrd_sch_id]; +		break; +	case V4L2_JPEG_CHROMA_SUBSAMPLING_422: +		if (dwngrd_sch_id > +				ARRAY_SIZE(subs422_fourcc_dwngrd_schema) - 1) +			return -EINVAL; +		*out_fourcc = subs422_fourcc_dwngrd_schema[dwngrd_sch_id]; +		break; +	default: +		*out_fourcc = V4L2_PIX_FMT_GREY; +		break; +	} + +	return 0; +} + +static int exynos4x12_decoded_subsampling[] = { +	V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, +	V4L2_JPEG_CHROMA_SUBSAMPLING_444, +	V4L2_JPEG_CHROMA_SUBSAMPLING_422, +	V4L2_JPEG_CHROMA_SUBSAMPLING_420, +}; +  static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)  {  	return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler); @@ -212,8 +467,24 @@ static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)  	return container_of(fh, struct s5p_jpeg_ctx, fh);  } -static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl, -		   unsigned long tab, int len) +static int s5p_jpeg_to_user_subsampling(struct s5p_jpeg_ctx *ctx) +{ +	WARN_ON(ctx->subsampling > 3); + +	if (ctx->jpeg->variant->version == SJPEG_S5P) { +		if (ctx->subsampling > 2) +			return V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; +		return ctx->subsampling; +	} else { +		if (ctx->subsampling > 2) +			return V4L2_JPEG_CHROMA_SUBSAMPLING_420; +		return exynos4x12_decoded_subsampling[ctx->subsampling]; +	} +} + +static inline void s5p_jpeg_set_qtbl(void __iomem *regs, +				     const unsigned char *qtbl, +				     unsigned long tab, int len)  {  	int i; @@ -221,22 +492,25 @@ static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,  		writel((unsigned int)qtbl[i], regs + tab + (i * 0x04));  } -static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_lum(void __iomem *regs, int quality)  {  	/* this driver fills quantisation table 0 with data for luma */ -	jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0), -		      ARRAY_SIZE(qtbl_luminance[quality])); +	s5p_jpeg_set_qtbl(regs, qtbl_luminance[quality], +			  S5P_JPG_QTBL_CONTENT(0), +			  ARRAY_SIZE(qtbl_luminance[quality]));  } -static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality) +static inline void s5p_jpeg_set_qtbl_chr(void __iomem *regs, int quality)  {  	/* this driver fills quantisation table 1 with data for chroma */ -	jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1), -		      ARRAY_SIZE(qtbl_chrominance[quality])); +	s5p_jpeg_set_qtbl(regs, qtbl_chrominance[quality], +			  S5P_JPG_QTBL_CONTENT(1), +			  ARRAY_SIZE(qtbl_chrominance[quality]));  } -static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl, -		   unsigned long tab, int len) +static inline void s5p_jpeg_set_htbl(void __iomem *regs, +				     const unsigned char *htbl, +				     unsigned long tab, int len)  {  	int i; @@ -244,28 +518,84 @@ static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,  		writel((unsigned int)htbl[i], regs + tab + (i * 0x04));  } -static inline void jpeg_set_hdctbl(void __iomem *regs) +static inline void s5p_jpeg_set_hdctbl(void __iomem *regs)  {  	/* this driver fills table 0 for this component */ -	jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0)); +	s5p_jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), +						ARRAY_SIZE(hdctbl0));  } -static inline void jpeg_set_hdctblg(void __iomem *regs) +static inline void s5p_jpeg_set_hdctblg(void __iomem *regs)  {  	/* this driver fills table 0 for this component */ -	jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0)); +	s5p_jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), +						ARRAY_SIZE(hdctblg0));  } -static inline void jpeg_set_hactbl(void __iomem *regs) +static inline void s5p_jpeg_set_hactbl(void __iomem *regs)  {  	/* this driver fills table 0 for this component */ -	jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0)); +	s5p_jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), +						ARRAY_SIZE(hactbl0));  } -static inline void jpeg_set_hactblg(void __iomem *regs) +static inline void s5p_jpeg_set_hactblg(void __iomem *regs)  {  	/* this driver fills table 0 for this component */ -	jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0)); +	s5p_jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), +						ARRAY_SIZE(hactblg0)); +} + +static inline void exynos4_jpeg_set_tbl(void __iomem *regs, +					const unsigned char *tbl, +					unsigned long tab, int len) +{ +	int i; +	unsigned int dword; + +	for (i = 0; i < len; i += 4) { +		dword = tbl[i] | +			(tbl[i + 1] << 8) | +			(tbl[i + 2] << 16) | +			(tbl[i + 3] << 24); +		writel(dword, regs + tab + i); +	} +} + +static inline void exynos4_jpeg_set_qtbl_lum(void __iomem *regs, int quality) +{ +	/* this driver fills quantisation table 0 with data for luma */ +	exynos4_jpeg_set_tbl(regs, qtbl_luminance[quality], +			     EXYNOS4_QTBL_CONTENT(0), +			     ARRAY_SIZE(qtbl_luminance[quality])); +} + +static inline void exynos4_jpeg_set_qtbl_chr(void __iomem *regs, int quality) +{ +	/* this driver fills quantisation table 1 with data for chroma */ +	exynos4_jpeg_set_tbl(regs, qtbl_chrominance[quality], +			     EXYNOS4_QTBL_CONTENT(1), +			     ARRAY_SIZE(qtbl_chrominance[quality])); +} + +void exynos4_jpeg_set_huff_tbl(void __iomem *base) +{ +	exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCLL, +							ARRAY_SIZE(hdctbl0)); +	exynos4_jpeg_set_tbl(base, hdctbl0, EXYNOS4_HUFF_TBL_HDCCL, +							ARRAY_SIZE(hdctbl0)); +	exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCLV, +							ARRAY_SIZE(hdctblg0)); +	exynos4_jpeg_set_tbl(base, hdctblg0, EXYNOS4_HUFF_TBL_HDCCV, +							ARRAY_SIZE(hdctblg0)); +	exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACLL, +							ARRAY_SIZE(hactbl0)); +	exynos4_jpeg_set_tbl(base, hactbl0, EXYNOS4_HUFF_TBL_HACCL, +							ARRAY_SIZE(hactbl0)); +	exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACLV, +							ARRAY_SIZE(hactblg0)); +	exynos4_jpeg_set_tbl(base, hactblg0, EXYNOS4_HUFF_TBL_HACCV, +							ARRAY_SIZE(hactblg0));  }  /* @@ -276,8 +606,8 @@ static inline void jpeg_set_hactblg(void __iomem *regs)  static int queue_init(void *priv, struct vb2_queue *src_vq,  		      struct vb2_queue *dst_vq); -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, -						 __u32 pixelformat); +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, +				__u32 pixelformat, unsigned int fmt_type);  static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);  static int s5p_jpeg_open(struct file *file) @@ -285,7 +615,7 @@ static int s5p_jpeg_open(struct file *file)  	struct s5p_jpeg *jpeg = video_drvdata(file);  	struct video_device *vfd = video_devdata(file);  	struct s5p_jpeg_ctx *ctx; -	struct s5p_jpeg_fmt *out_fmt; +	struct s5p_jpeg_fmt *out_fmt, *cap_fmt;  	int ret = 0;  	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -306,24 +636,31 @@ static int s5p_jpeg_open(struct file *file)  	ctx->jpeg = jpeg;  	if (vfd == jpeg->vfd_encoder) {  		ctx->mode = S5P_JPEG_ENCODE; -		out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565); +		out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_RGB565, +							FMT_TYPE_OUTPUT); +		cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, +							FMT_TYPE_CAPTURE);  	} else {  		ctx->mode = S5P_JPEG_DECODE; -		out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG); +		out_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, +							FMT_TYPE_OUTPUT); +		cap_fmt = s5p_jpeg_find_format(ctx, V4L2_PIX_FMT_YUYV, +							FMT_TYPE_CAPTURE);  	} -	ret = s5p_jpeg_controls_create(ctx); -	if (ret < 0) -		goto error; - -	ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); -	if (IS_ERR(ctx->m2m_ctx)) { -		ret = PTR_ERR(ctx->m2m_ctx); +	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init); +	if (IS_ERR(ctx->fh.m2m_ctx)) { +		ret = PTR_ERR(ctx->fh.m2m_ctx);  		goto error;  	}  	ctx->out_q.fmt = out_fmt; -	ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV); +	ctx->cap_q.fmt = cap_fmt; + +	ret = s5p_jpeg_controls_create(ctx); +	if (ret < 0) +		goto error; +  	mutex_unlock(&jpeg->lock);  	return 0; @@ -342,49 +679,23 @@ static int s5p_jpeg_release(struct file *file)  	struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);  	mutex_lock(&jpeg->lock); -	v4l2_m2m_ctx_release(ctx->m2m_ctx); -	mutex_unlock(&jpeg->lock); +	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);  	v4l2_ctrl_handler_free(&ctx->ctrl_handler);  	v4l2_fh_del(&ctx->fh);  	v4l2_fh_exit(&ctx->fh);  	kfree(ctx); - -	return 0; -} - -static unsigned int s5p_jpeg_poll(struct file *file, -				 struct poll_table_struct *wait) -{ -	struct s5p_jpeg *jpeg = video_drvdata(file); -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); -	unsigned int res; - -	mutex_lock(&jpeg->lock); -	res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);  	mutex_unlock(&jpeg->lock); -	return res; -} - -static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma) -{ -	struct s5p_jpeg *jpeg = video_drvdata(file); -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data); -	int ret; -	if (mutex_lock_interruptible(&jpeg->lock)) -		return -ERESTARTSYS; -	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -	mutex_unlock(&jpeg->lock); -	return ret; +	return 0;  }  static const struct v4l2_file_operations s5p_jpeg_fops = {  	.owner		= THIS_MODULE,  	.open		= s5p_jpeg_open,  	.release	= s5p_jpeg_release, -	.poll		= s5p_jpeg_poll, +	.poll		= v4l2_m2m_fop_poll,  	.unlocked_ioctl	= video_ioctl2, -	.mmap		= s5p_jpeg_mmap, +	.mmap		= v4l2_m2m_fop_mmap,  };  /* @@ -427,10 +738,11 @@ static void skip(struct s5p_jpeg_buffer *buf, long len)  }  static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result, -			       unsigned long buffer, unsigned long size) +			       unsigned long buffer, unsigned long size, +			       struct s5p_jpeg_ctx *ctx)  {  	int c, components, notfound; -	unsigned int height, width, word; +	unsigned int height, width, word, subsampling = 0;  	long length;  	struct s5p_jpeg_buffer jpeg_buffer; @@ -469,7 +781,15 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,  				break;  			notfound = 0; -			skip(&jpeg_buffer, components * 3); +			if (components == 1) { +				subsampling = 0x33; +			} else { +				skip(&jpeg_buffer, 1); +				subsampling = get_byte(&jpeg_buffer); +				skip(&jpeg_buffer, 1); +			} + +			skip(&jpeg_buffer, components * 2);  			break;  		/* skip payload-less markers */ @@ -491,6 +811,24 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,  	result->w = width;  	result->h = height;  	result->size = components; + +	switch (subsampling) { +	case 0x11: +		ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_444; +		break; +	case 0x21: +		ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422; +		break; +	case 0x22: +		ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420; +		break; +	case 0x33: +		ctx->subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; +		break; +	default: +		return false; +	} +  	return !notfound;  } @@ -521,13 +859,13 @@ static int s5p_jpeg_querycap(struct file *file, void *priv,  	return 0;  } -static int enum_fmt(struct s5p_jpeg_fmt *formats, int n, +static int enum_fmt(struct s5p_jpeg_fmt *sjpeg_formats, int n,  		    struct v4l2_fmtdesc *f, u32 type)  {  	int i, num = 0;  	for (i = 0; i < n; ++i) { -		if (formats[i].types & type) { +		if (sjpeg_formats[i].flags & type) {  			/* index-th format of type type found ? */  			if (num == f->index)  				break; @@ -541,8 +879,8 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,  	if (i >= n)  		return -EINVAL; -	strlcpy(f->description, formats[i].name, sizeof(f->description)); -	f->pixelformat = formats[i].fourcc; +	strlcpy(f->description, sjpeg_formats[i].name, sizeof(f->description)); +	f->pixelformat = sjpeg_formats[i].fourcc;  	return 0;  } @@ -553,10 +891,11 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,  	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);  	if (ctx->mode == S5P_JPEG_ENCODE) -		return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, -				MEM2MEM_CAPTURE); +		return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, +				SJPEG_FMT_FLAG_ENC_CAPTURE); -	return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE); +	return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, +					SJPEG_FMT_FLAG_DEC_CAPTURE);  }  static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv, @@ -565,10 +904,11 @@ static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,  	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);  	if (ctx->mode == S5P_JPEG_ENCODE) -		return enum_fmt(formats_enc, NUM_FORMATS_ENC, f, -				MEM2MEM_OUTPUT); +		return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, +				SJPEG_FMT_FLAG_ENC_OUTPUT); -	return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT); +	return enum_fmt(sjpeg_formats, SJPEG_NUM_FORMATS, f, +					SJPEG_FMT_FLAG_DEC_OUTPUT);  }  static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx, @@ -589,7 +929,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)  	struct v4l2_pix_format *pix = &f->fmt.pix;  	struct s5p_jpeg_ctx *ct = fh_to_ctx(priv); -	vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);  	if (!vq)  		return -EINVAL; @@ -615,29 +955,30 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)  	return 0;  } -static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode, -						 u32 pixelformat) +static struct s5p_jpeg_fmt *s5p_jpeg_find_format(struct s5p_jpeg_ctx *ctx, +				u32 pixelformat, unsigned int fmt_type)  { -	unsigned int k; -	struct s5p_jpeg_fmt *formats; -	int n; +	unsigned int k, fmt_flag; -	if (mode == S5P_JPEG_ENCODE) { -		formats = formats_enc; -		n = NUM_FORMATS_ENC; -	} else { -		formats = formats_dec; -		n = NUM_FORMATS_DEC; -	} - -	for (k = 0; k < n; k++) { -		struct s5p_jpeg_fmt *fmt = &formats[k]; -		if (fmt->fourcc == pixelformat) +	if (ctx->mode == S5P_JPEG_ENCODE) +		fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? +				SJPEG_FMT_FLAG_ENC_OUTPUT : +				SJPEG_FMT_FLAG_ENC_CAPTURE; +	else +		fmt_flag = (fmt_type == FMT_TYPE_OUTPUT) ? +				SJPEG_FMT_FLAG_DEC_OUTPUT : +				SJPEG_FMT_FLAG_DEC_CAPTURE; + +	for (k = 0; k < ARRAY_SIZE(sjpeg_formats); k++) { +		struct s5p_jpeg_fmt *fmt = &sjpeg_formats[k]; +		if (fmt->fourcc == pixelformat && +		    fmt->flags & fmt_flag && +		    fmt->flags & ctx->jpeg->variant->fmt_ver_flag) {  			return fmt; +		}  	}  	return NULL; -  }  static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, @@ -673,7 +1014,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,  	/* V4L2 specification suggests the driver corrects the format struct  	 * if any of the dimensions is unsupported */ -	if (q_type == MEM2MEM_OUTPUT) +	if (q_type == FMT_TYPE_OUTPUT)  		jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,  				       S5P_JPEG_MAX_WIDTH, 0,  				       &pix->height, S5P_JPEG_MIN_HEIGHT, @@ -695,7 +1036,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,  			bpl = pix->width; /* planar */  		if (fmt->colplanes == 1 && /* packed */ -		    (bpl << 3) * fmt->depth < pix->width) +		    (bpl << 3) / fmt->depth < pix->width)  			bpl = (pix->width * fmt->depth) >> 3;  		pix->bytesperline = bpl; @@ -709,17 +1050,60 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,  				  struct v4l2_format *f)  {  	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); +	struct v4l2_pix_format *pix = &f->fmt.pix;  	struct s5p_jpeg_fmt *fmt; +	int ret; -	fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); -	if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { +	fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, +						FMT_TYPE_CAPTURE); +	if (!fmt) {  		v4l2_err(&ctx->jpeg->v4l2_dev,  			 "Fourcc format (0x%08x) invalid.\n",  			 f->fmt.pix.pixelformat);  		return -EINVAL;  	} -	return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE); +	if ((ctx->jpeg->variant->version != SJPEG_EXYNOS4) || +	    (ctx->mode != S5P_JPEG_DECODE)) +		goto exit; + +	/* +	 * The exynos4x12 device requires resulting YUV image +	 * subsampling not to be lower than the input jpeg subsampling. +	 * If this requirement is not met then downgrade the requested +	 * capture format to the one with subsampling equal to the input jpeg. +	 */ +	if ((fmt->flags & SJPEG_FMT_NON_RGB) && +	    (fmt->subsampling < ctx->subsampling)) { +		ret = s5p_jpeg_adjust_fourcc_to_subsampling(ctx->subsampling, +							    fmt->fourcc, +							    &pix->pixelformat, +							    ctx); +		if (ret < 0) +			pix->pixelformat = V4L2_PIX_FMT_GREY; + +		fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, +							FMT_TYPE_CAPTURE); +	} + +	/* +	 * Decompression of a JPEG file with 4:2:0 subsampling and odd +	 * width to the YUV 4:2:0 compliant formats produces a raw image +	 * with broken luma component. Adjust capture format to RGB565 +	 * in such a case. +	 */ +	if (ctx->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_420 && +	    (ctx->out_q.w & 1) && +	    (pix->pixelformat == V4L2_PIX_FMT_NV12 || +	     pix->pixelformat == V4L2_PIX_FMT_NV21 || +	     pix->pixelformat == V4L2_PIX_FMT_YUV420)) { +		pix->pixelformat = V4L2_PIX_FMT_RGB565; +		fmt = s5p_jpeg_find_format(ctx, pix->pixelformat, +							FMT_TYPE_CAPTURE); +	} + +exit: +	return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_CAPTURE);  }  static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv, @@ -728,15 +1112,42 @@ static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,  	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);  	struct s5p_jpeg_fmt *fmt; -	fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat); -	if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { +	fmt = s5p_jpeg_find_format(ctx, f->fmt.pix.pixelformat, +						FMT_TYPE_OUTPUT); +	if (!fmt) {  		v4l2_err(&ctx->jpeg->v4l2_dev,  			 "Fourcc format (0x%08x) invalid.\n",  			 f->fmt.pix.pixelformat);  		return -EINVAL;  	} -	return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT); +	return vidioc_try_fmt(f, fmt, ctx, FMT_TYPE_OUTPUT); +} + +static int exynos4_jpeg_get_output_buffer_size(struct s5p_jpeg_ctx *ctx, +						struct v4l2_format *f, +						int fmt_depth) +{ +	struct v4l2_pix_format *pix = &f->fmt.pix; +	u32 pix_fmt = f->fmt.pix.pixelformat; +	int w = pix->width, h = pix->height, wh_align; + +	if (pix_fmt == V4L2_PIX_FMT_RGB32 || +	    pix_fmt == V4L2_PIX_FMT_NV24 || +	    pix_fmt == V4L2_PIX_FMT_NV42 || +	    pix_fmt == V4L2_PIX_FMT_NV12 || +	    pix_fmt == V4L2_PIX_FMT_NV21 || +	    pix_fmt == V4L2_PIX_FMT_YUV420) +		wh_align = 4; +	else +		wh_align = 1; + +	jpeg_bound_align_image(&w, S5P_JPEG_MIN_WIDTH, +			       S5P_JPEG_MAX_WIDTH, wh_align, +			       &h, S5P_JPEG_MIN_HEIGHT, +			       S5P_JPEG_MAX_HEIGHT, wh_align); + +	return w * h * fmt_depth >> 3;  }  static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f) @@ -744,8 +1155,10 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)  	struct vb2_queue *vq;  	struct s5p_jpeg_q_data *q_data = NULL;  	struct v4l2_pix_format *pix = &f->fmt.pix; +	struct v4l2_ctrl *ctrl_subs; +	unsigned int f_type; -	vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type); +	vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);  	if (!vq)  		return -EINVAL; @@ -757,13 +1170,37 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)  		return -EBUSY;  	} -	q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat); +	f_type = V4L2_TYPE_IS_OUTPUT(f->type) ? +			FMT_TYPE_OUTPUT : FMT_TYPE_CAPTURE; + +	q_data->fmt = s5p_jpeg_find_format(ct, pix->pixelformat, f_type);  	q_data->w = pix->width;  	q_data->h = pix->height; -	if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) -		q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3; -	else +	if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) { +		/* +		 * During encoding Exynos4x12 SoCs access wider memory area +		 * than it results from Image_x and Image_y values written to +		 * the JPEG_IMAGE_SIZE register. In order to avoid sysmmu +		 * page fault calculate proper buffer size in such a case. +		 */ +		if (ct->jpeg->variant->version == SJPEG_EXYNOS4 && +		    f_type == FMT_TYPE_OUTPUT && ct->mode == S5P_JPEG_ENCODE) +			q_data->size = exynos4_jpeg_get_output_buffer_size(ct, +							f, +							q_data->fmt->depth); +		else +			q_data->size = q_data->w * q_data->h * +						q_data->fmt->depth >> 3; +	} else {  		q_data->size = pix->sizeimage; +	} + +	if (f_type == FMT_TYPE_OUTPUT) { +		ctrl_subs = v4l2_ctrl_find(&ct->ctrl_handler, +					V4L2_CID_JPEG_CHROMA_SUBSAMPLING); +		if (ctrl_subs) +			v4l2_ctrl_s_ctrl(ctrl_subs, q_data->fmt->subsampling); +	}  	return 0;  } @@ -792,53 +1229,6 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,  	return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);  } -static int s5p_jpeg_reqbufs(struct file *file, void *priv, -			  struct v4l2_requestbuffers *reqbufs) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int s5p_jpeg_querybuf(struct file *file, void *priv, -			   struct v4l2_buffer *buf) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_dqbuf(struct file *file, void *priv, -			  struct v4l2_buffer *buf) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int s5p_jpeg_streamon(struct file *file, void *priv, -			   enum v4l2_buf_type type) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int s5p_jpeg_streamoff(struct file *file, void *priv, -			    enum v4l2_buf_type type) -{ -	struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); - -	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} -  static int s5p_jpeg_g_selection(struct file *file, void *priv,  			 struct v4l2_selection *s)  { @@ -884,12 +1274,7 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)  	switch (ctrl->id) {  	case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:  		spin_lock_irqsave(&jpeg->slock, flags); - -		WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY); -		if (ctx->subsampling > 2) -			ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY; -		else -			ctrl->val = ctx->subsampling; +		ctrl->val = s5p_jpeg_to_user_subsampling(ctx);  		spin_unlock_irqrestore(&jpeg->slock, flags);  		break;  	} @@ -897,6 +1282,40 @@ static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)  	return 0;  } +static int s5p_jpeg_try_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); +	unsigned long flags; +	int ret = 0; + +	spin_lock_irqsave(&ctx->jpeg->slock, flags); + +	if (ctrl->id == V4L2_CID_JPEG_CHROMA_SUBSAMPLING) { +		if (ctx->jpeg->variant->version == SJPEG_S5P) +			goto error_free; +		/* +		 * The exynos4x12 device requires input raw image fourcc +		 * to be V4L2_PIX_FMT_GREY if gray jpeg format +		 * is to be set. +		 */ +		if (ctx->out_q.fmt->fourcc != V4L2_PIX_FMT_GREY && +		    ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY) { +			ret = -EINVAL; +			goto error_free; +		} +		/* +		 * The exynos4x12 device requires resulting jpeg subsampling +		 * not to be lower than the input raw image subsampling. +		 */ +		if (ctx->out_q.fmt->subsampling > ctrl->val) +			ctrl->val = ctx->out_q.fmt->subsampling; +	} + +error_free: +	spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +	return ret; +} +  static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)  {  	struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); @@ -906,7 +1325,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)  	switch (ctrl->id) {  	case V4L2_CID_JPEG_COMPRESSION_QUALITY: -		ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val; +		ctx->compr_quality = ctrl->val;  		break;  	case V4L2_CID_JPEG_RESTART_INTERVAL:  		ctx->restart_interval = ctrl->val; @@ -922,6 +1341,7 @@ static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)  static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {  	.g_volatile_ctrl	= s5p_jpeg_g_volatile_ctrl, +	.try_ctrl		= s5p_jpeg_try_ctrl,  	.s_ctrl			= s5p_jpeg_s_ctrl,  }; @@ -929,18 +1349,20 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)  {  	unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */  	struct v4l2_ctrl *ctrl; +	int ret;  	v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);  	if (ctx->mode == S5P_JPEG_ENCODE) {  		v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,  				  V4L2_CID_JPEG_COMPRESSION_QUALITY, -				  0, 3, 1, 3); +				  0, 3, 1, S5P_JPEG_COMPR_QUAL_WORST);  		v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,  				  V4L2_CID_JPEG_RESTART_INTERVAL,  				  0, 3, 0xffff, 0); -		mask = ~0x06; /* 422, 420 */ +		if (ctx->jpeg->variant->version == SJPEG_S5P) +			mask = ~0x06; /* 422, 420 */  	}  	ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops, @@ -948,13 +1370,24 @@ static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)  				      V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,  				      V4L2_JPEG_CHROMA_SUBSAMPLING_422); -	if (ctx->ctrl_handler.error) -		return ctx->ctrl_handler.error; +	if (ctx->ctrl_handler.error) { +		ret = ctx->ctrl_handler.error; +		goto error_free; +	}  	if (ctx->mode == S5P_JPEG_DECODE)  		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |  			V4L2_CTRL_FLAG_READ_ONLY; -	return 0; + +	ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); +	if (ret < 0) +		goto error_free; + +	return ret; + +error_free: +	v4l2_ctrl_handler_free(&ctx->ctrl_handler); +	return ret;  }  static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = { @@ -972,14 +1405,13 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {  	.vidioc_s_fmt_vid_cap		= s5p_jpeg_s_fmt_vid_cap,  	.vidioc_s_fmt_vid_out		= s5p_jpeg_s_fmt_vid_out, -	.vidioc_reqbufs			= s5p_jpeg_reqbufs, -	.vidioc_querybuf		= s5p_jpeg_querybuf, +	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs, +	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf, +	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf, +	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf, -	.vidioc_qbuf			= s5p_jpeg_qbuf, -	.vidioc_dqbuf			= s5p_jpeg_dqbuf, - -	.vidioc_streamon		= s5p_jpeg_streamon, -	.vidioc_streamoff		= s5p_jpeg_streamoff, +	.vidioc_streamon		= v4l2_m2m_ioctl_streamon, +	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,  	.vidioc_g_selection		= s5p_jpeg_g_selection,  }; @@ -995,74 +1427,181 @@ static void s5p_jpeg_device_run(void *priv)  	struct s5p_jpeg_ctx *ctx = priv;  	struct s5p_jpeg *jpeg = ctx->jpeg;  	struct vb2_buffer *src_buf, *dst_buf; -	unsigned long src_addr, dst_addr; +	unsigned long src_addr, dst_addr, flags; -	src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); -	dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); +	spin_lock_irqsave(&ctx->jpeg->slock, flags); + +	src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); +	dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);  	src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);  	dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); -	jpeg_reset(jpeg->regs); -	jpeg_poweron(jpeg->regs); -	jpeg_proc_mode(jpeg->regs, ctx->mode); +	s5p_jpeg_reset(jpeg->regs); +	s5p_jpeg_poweron(jpeg->regs); +	s5p_jpeg_proc_mode(jpeg->regs, ctx->mode);  	if (ctx->mode == S5P_JPEG_ENCODE) {  		if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565) -			jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565); +			s5p_jpeg_input_raw_mode(jpeg->regs, +							S5P_JPEG_RAW_IN_565);  		else -			jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422); -		jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); -		jpeg_dri(jpeg->regs, ctx->restart_interval); -		jpeg_x(jpeg->regs, ctx->out_q.w); -		jpeg_y(jpeg->regs, ctx->out_q.h); -		jpeg_imgadr(jpeg->regs, src_addr); -		jpeg_jpgadr(jpeg->regs, dst_addr); +			s5p_jpeg_input_raw_mode(jpeg->regs, +							S5P_JPEG_RAW_IN_422); +		s5p_jpeg_subsampling_mode(jpeg->regs, ctx->subsampling); +		s5p_jpeg_dri(jpeg->regs, ctx->restart_interval); +		s5p_jpeg_x(jpeg->regs, ctx->out_q.w); +		s5p_jpeg_y(jpeg->regs, ctx->out_q.h); +		s5p_jpeg_imgadr(jpeg->regs, src_addr); +		s5p_jpeg_jpgadr(jpeg->regs, dst_addr);  		/* ultimately comes from sizeimage from userspace */ -		jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size); +		s5p_jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);  		/* JPEG RGB to YCbCr conversion matrix */ -		jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); -		jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); -		jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); -		jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); -		jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); -		jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); -		jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); -		jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); -		jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33); +		s5p_jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11); +		s5p_jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12); +		s5p_jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13); +		s5p_jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21); +		s5p_jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22); +		s5p_jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23); +		s5p_jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31); +		s5p_jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32); +		s5p_jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);  		/*  		 * JPEG IP allows storing 4 quantization tables  		 * We fill table 0 for luma and table 1 for chroma  		 */ -		jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); -		jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); +		s5p_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); +		s5p_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);  		/* use table 0 for Y */ -		jpeg_qtbl(jpeg->regs, 1, 0); +		s5p_jpeg_qtbl(jpeg->regs, 1, 0);  		/* use table 1 for Cb and Cr*/ -		jpeg_qtbl(jpeg->regs, 2, 1); -		jpeg_qtbl(jpeg->regs, 3, 1); +		s5p_jpeg_qtbl(jpeg->regs, 2, 1); +		s5p_jpeg_qtbl(jpeg->regs, 3, 1);  		/* Y, Cb, Cr use Huffman table 0 */ -		jpeg_htbl_ac(jpeg->regs, 1); -		jpeg_htbl_dc(jpeg->regs, 1); -		jpeg_htbl_ac(jpeg->regs, 2); -		jpeg_htbl_dc(jpeg->regs, 2); -		jpeg_htbl_ac(jpeg->regs, 3); -		jpeg_htbl_dc(jpeg->regs, 3); +		s5p_jpeg_htbl_ac(jpeg->regs, 1); +		s5p_jpeg_htbl_dc(jpeg->regs, 1); +		s5p_jpeg_htbl_ac(jpeg->regs, 2); +		s5p_jpeg_htbl_dc(jpeg->regs, 2); +		s5p_jpeg_htbl_ac(jpeg->regs, 3); +		s5p_jpeg_htbl_dc(jpeg->regs, 3);  	} else { /* S5P_JPEG_DECODE */ -		jpeg_rst_int_enable(jpeg->regs, true); -		jpeg_data_num_int_enable(jpeg->regs, true); -		jpeg_final_mcu_num_int_enable(jpeg->regs, true); +		s5p_jpeg_rst_int_enable(jpeg->regs, true); +		s5p_jpeg_data_num_int_enable(jpeg->regs, true); +		s5p_jpeg_final_mcu_num_int_enable(jpeg->regs, true);  		if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV) -			jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); +			s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422); +		else +			s5p_jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); +		s5p_jpeg_jpgadr(jpeg->regs, src_addr); +		s5p_jpeg_imgadr(jpeg->regs, dst_addr); +	} + +	s5p_jpeg_start(jpeg->regs); + +	spin_unlock_irqrestore(&ctx->jpeg->slock, flags); +} + +static void exynos4_jpeg_set_img_addr(struct s5p_jpeg_ctx *ctx) +{ +	struct s5p_jpeg *jpeg = ctx->jpeg; +	struct s5p_jpeg_fmt *fmt; +	struct vb2_buffer *vb; +	struct s5p_jpeg_addr jpeg_addr; +	u32 pix_size, padding_bytes = 0; + +	pix_size = ctx->cap_q.w * ctx->cap_q.h; + +	if (ctx->mode == S5P_JPEG_ENCODE) { +		vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); +		fmt = ctx->out_q.fmt; +		if (ctx->out_q.w % 2 && fmt->h_align > 0) +			padding_bytes = ctx->out_q.h; +	} else { +		fmt = ctx->cap_q.fmt; +		vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); +	} + +	jpeg_addr.y = vb2_dma_contig_plane_dma_addr(vb, 0); + +	if (fmt->colplanes == 2) { +		jpeg_addr.cb = jpeg_addr.y + pix_size - padding_bytes; +	} else if (fmt->colplanes == 3) { +		jpeg_addr.cb = jpeg_addr.y + pix_size; +		if (fmt->fourcc == V4L2_PIX_FMT_YUV420) +			jpeg_addr.cr = jpeg_addr.cb + pix_size / 4;  		else -			jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420); -		jpeg_jpgadr(jpeg->regs, src_addr); -		jpeg_imgadr(jpeg->regs, dst_addr); +			jpeg_addr.cr = jpeg_addr.cb + pix_size / 2;  	} -	jpeg_start(jpeg->regs); +	exynos4_jpeg_set_frame_buf_address(jpeg->regs, &jpeg_addr); +} + +static void exynos4_jpeg_set_jpeg_addr(struct s5p_jpeg_ctx *ctx) +{ +	struct s5p_jpeg *jpeg = ctx->jpeg; +	struct vb2_buffer *vb; +	unsigned int jpeg_addr = 0; + +	if (ctx->mode == S5P_JPEG_ENCODE) +		vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); +	else +		vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + +	jpeg_addr = vb2_dma_contig_plane_dma_addr(vb, 0); +	exynos4_jpeg_set_stream_buf_address(jpeg->regs, jpeg_addr); +} + +static void exynos4_jpeg_device_run(void *priv) +{ +	struct s5p_jpeg_ctx *ctx = priv; +	struct s5p_jpeg *jpeg = ctx->jpeg; +	unsigned int bitstream_size; +	unsigned long flags; + +	spin_lock_irqsave(&ctx->jpeg->slock, flags); + +	if (ctx->mode == S5P_JPEG_ENCODE) { +		exynos4_jpeg_sw_reset(jpeg->regs); +		exynos4_jpeg_set_interrupt(jpeg->regs); +		exynos4_jpeg_set_huf_table_enable(jpeg->regs, 1); + +		exynos4_jpeg_set_huff_tbl(jpeg->regs); + +		/* +		 * JPEG IP allows storing 4 quantization tables +		 * We fill table 0 for luma and table 1 for chroma +		 */ +		exynos4_jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality); +		exynos4_jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality); + +		exynos4_jpeg_set_encode_tbl_select(jpeg->regs, +							ctx->compr_quality); +		exynos4_jpeg_set_stream_size(jpeg->regs, ctx->cap_q.w, +							ctx->cap_q.h); + +		exynos4_jpeg_set_enc_out_fmt(jpeg->regs, ctx->subsampling); +		exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->out_q.fmt->fourcc); +		exynos4_jpeg_set_img_addr(ctx); +		exynos4_jpeg_set_jpeg_addr(ctx); +		exynos4_jpeg_set_encode_hoff_cnt(jpeg->regs, +							ctx->out_q.fmt->fourcc); +	} else { +		exynos4_jpeg_sw_reset(jpeg->regs); +		exynos4_jpeg_set_interrupt(jpeg->regs); +		exynos4_jpeg_set_img_addr(ctx); +		exynos4_jpeg_set_jpeg_addr(ctx); +		exynos4_jpeg_set_img_fmt(jpeg->regs, ctx->cap_q.fmt->fourcc); + +		bitstream_size = DIV_ROUND_UP(ctx->out_q.size, 32); + +		exynos4_jpeg_set_dec_bitstream_size(jpeg->regs, bitstream_size); +	} + +	exynos4_jpeg_set_enc_dec_mode(jpeg->regs, ctx->mode); + +	spin_unlock_irqrestore(&ctx->jpeg->slock, flags);  }  static int s5p_jpeg_job_ready(void *priv) @@ -1082,6 +1621,12 @@ static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {  	.device_run	= s5p_jpeg_device_run,  	.job_ready	= s5p_jpeg_job_ready,  	.job_abort	= s5p_jpeg_job_abort, +} +; +static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = { +	.device_run	= exynos4_jpeg_device_run, +	.job_ready	= s5p_jpeg_job_ready, +	.job_abort	= s5p_jpeg_job_abort,  };  /* @@ -1149,7 +1694,7 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)  		ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp,  		     (unsigned long)vb2_plane_vaddr(vb, 0),  		     min((unsigned long)ctx->out_q.size, -			 vb2_get_plane_payload(vb, 0))); +			 vb2_get_plane_payload(vb, 0)), ctx);  		if (!ctx->hdr_parsed) {  			vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);  			return; @@ -1162,30 +1707,9 @@ static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)  		q_data = &ctx->cap_q;  		q_data->w = tmp.w;  		q_data->h = tmp.h; - -		jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH, -				       S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align, -				       &q_data->h, S5P_JPEG_MIN_HEIGHT, -				       S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align -				      ); -		q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;  	} -	if (ctx->m2m_ctx) -		v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} -static void s5p_jpeg_wait_prepare(struct vb2_queue *vq) -{ -	struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - -	mutex_unlock(&ctx->jpeg->lock); -} - -static void s5p_jpeg_wait_finish(struct vb2_queue *vq) -{ -	struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq); - -	mutex_lock(&ctx->jpeg->lock); +	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);  }  static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) @@ -1198,21 +1722,19 @@ static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)  	return ret > 0 ? 0 : ret;  } -static int s5p_jpeg_stop_streaming(struct vb2_queue *q) +static void s5p_jpeg_stop_streaming(struct vb2_queue *q)  {  	struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);  	pm_runtime_put(ctx->jpeg->dev); - -	return 0;  }  static struct vb2_ops s5p_jpeg_qops = {  	.queue_setup		= s5p_jpeg_queue_setup,  	.buf_prepare		= s5p_jpeg_buf_prepare,  	.buf_queue		= s5p_jpeg_buf_queue, -	.wait_prepare		= s5p_jpeg_wait_prepare, -	.wait_finish		= s5p_jpeg_wait_finish, +	.wait_prepare		= vb2_ops_wait_prepare, +	.wait_finish		= vb2_ops_wait_finish,  	.start_streaming	= s5p_jpeg_start_streaming,  	.stop_streaming		= s5p_jpeg_stop_streaming,  }; @@ -1229,7 +1751,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	src_vq->ops = &s5p_jpeg_qops;  	src_vq->mem_ops = &vb2_dma_contig_memops; -	src_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	src_vq->lock = &ctx->jpeg->lock;  	ret = vb2_queue_init(src_vq);  	if (ret) @@ -1241,7 +1764,8 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,  	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);  	dst_vq->ops = &s5p_jpeg_qops;  	dst_vq->mem_ops = &vb2_dma_contig_memops; -	dst_vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	dst_vq->lock = &ctx->jpeg->lock;  	return vb2_queue_init(dst_vq);  } @@ -1267,45 +1791,112 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)  	curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); -	src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); -	dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); +	src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); +	dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);  	if (curr_ctx->mode == S5P_JPEG_ENCODE) -		enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs); -	timer_elapsed = jpeg_timer_stat(jpeg->regs); -	op_completed = jpeg_result_stat_ok(jpeg->regs); +		enc_jpeg_too_large = s5p_jpeg_enc_stream_stat(jpeg->regs); +	timer_elapsed = s5p_jpeg_timer_stat(jpeg->regs); +	op_completed = s5p_jpeg_result_stat_ok(jpeg->regs);  	if (curr_ctx->mode == S5P_JPEG_DECODE) -		op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs); +		op_completed = op_completed && +					s5p_jpeg_stream_stat_ok(jpeg->regs);  	if (enc_jpeg_too_large) {  		state = VB2_BUF_STATE_ERROR; -		jpeg_clear_enc_stream_stat(jpeg->regs); +		s5p_jpeg_clear_enc_stream_stat(jpeg->regs);  	} else if (timer_elapsed) {  		state = VB2_BUF_STATE_ERROR; -		jpeg_clear_timer_stat(jpeg->regs); +		s5p_jpeg_clear_timer_stat(jpeg->regs);  	} else if (!op_completed) {  		state = VB2_BUF_STATE_ERROR;  	} else { -		payload_size = jpeg_compressed_size(jpeg->regs); +		payload_size = s5p_jpeg_compressed_size(jpeg->regs);  	}  	dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;  	dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp; +	dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +	dst_buf->v4l2_buf.flags |= +		src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  	v4l2_m2m_buf_done(src_buf, state);  	if (curr_ctx->mode == S5P_JPEG_ENCODE)  		vb2_set_plane_payload(dst_buf, 0, payload_size);  	v4l2_m2m_buf_done(dst_buf, state); -	v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx); +	v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); -	curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs); +	curr_ctx->subsampling = s5p_jpeg_get_subsampling_mode(jpeg->regs);  	spin_unlock(&jpeg->slock); -	jpeg_clear_int(jpeg->regs); +	s5p_jpeg_clear_int(jpeg->regs); + +	return IRQ_HANDLED; +} + +static irqreturn_t exynos4_jpeg_irq(int irq, void *priv) +{ +	unsigned int int_status; +	struct vb2_buffer *src_vb, *dst_vb; +	struct s5p_jpeg *jpeg = priv; +	struct s5p_jpeg_ctx *curr_ctx; +	unsigned long payload_size = 0; + +	spin_lock(&jpeg->slock); + +	curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + +	src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); +	dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); + +	int_status = exynos4_jpeg_get_int_status(jpeg->regs); + +	if (int_status) { +		switch (int_status & 0x1f) { +		case 0x1: +			jpeg->irq_ret = ERR_PROT; +			break; +		case 0x2: +			jpeg->irq_ret = OK_ENC_OR_DEC; +			break; +		case 0x4: +			jpeg->irq_ret = ERR_DEC_INVALID_FORMAT; +			break; +		case 0x8: +			jpeg->irq_ret = ERR_MULTI_SCAN; +			break; +		case 0x10: +			jpeg->irq_ret = ERR_FRAME; +			break; +		default: +			jpeg->irq_ret = ERR_UNKNOWN; +			break; +		} +	} else { +		jpeg->irq_ret = ERR_UNKNOWN; +	} + +	if (jpeg->irq_ret == OK_ENC_OR_DEC) { +		if (curr_ctx->mode == S5P_JPEG_ENCODE) { +			payload_size = exynos4_jpeg_get_stream_size(jpeg->regs); +			vb2_set_plane_payload(dst_vb, 0, payload_size); +		} +		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); +		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); +	} else { +		v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_ERROR); +		v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_ERROR); +	} + +	v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->fh.m2m_ctx); +	curr_ctx->subsampling = exynos4_jpeg_get_frame_fmt(jpeg->regs); +	spin_unlock(&jpeg->slock);  	return IRQ_HANDLED;  } +static void *jpeg_get_drv_data(struct device *dev); +  /*   * ============================================================================   * Driver basic infrastructure @@ -1323,6 +1914,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  	if (!jpeg)  		return -ENOMEM; +	jpeg->variant = jpeg_get_drv_data(&pdev->dev); +  	mutex_init(&jpeg->lock);  	spin_lock_init(&jpeg->slock);  	jpeg->dev = &pdev->dev; @@ -1341,8 +1934,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  		return ret;  	} -	ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0, -			dev_name(&pdev->dev), jpeg); +	ret = devm_request_irq(&pdev->dev, jpeg->irq, jpeg->variant->jpeg_irq, +				0, dev_name(&pdev->dev), jpeg);  	if (ret) {  		dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);  		return ret; @@ -1356,7 +1949,6 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  		return ret;  	}  	dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); -	clk_prepare_enable(jpeg->clk);  	/* v4l2 device */  	ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev); @@ -1366,7 +1958,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  	}  	/* mem2mem device */ -	jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops); +	jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops);  	if (IS_ERR(jpeg->m2m_dev)) {  		v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");  		ret = PTR_ERR(jpeg->m2m_dev); @@ -1387,8 +1979,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  		ret = -ENOMEM;  		goto vb2_allocator_rollback;  	} -	strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME, -		sizeof(jpeg->vfd_encoder->name)); +	snprintf(jpeg->vfd_encoder->name, sizeof(jpeg->vfd_encoder->name), +				"%s-enc", S5P_JPEG_M2M_NAME);  	jpeg->vfd_encoder->fops		= &s5p_jpeg_fops;  	jpeg->vfd_encoder->ioctl_ops	= &s5p_jpeg_ioctl_ops;  	jpeg->vfd_encoder->minor	= -1; @@ -1415,14 +2007,15 @@ static int s5p_jpeg_probe(struct platform_device *pdev)  		ret = -ENOMEM;  		goto enc_vdev_register_rollback;  	} -	strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME, -		sizeof(jpeg->vfd_decoder->name)); +	snprintf(jpeg->vfd_decoder->name, sizeof(jpeg->vfd_decoder->name), +				"%s-dec", S5P_JPEG_M2M_NAME);  	jpeg->vfd_decoder->fops		= &s5p_jpeg_fops;  	jpeg->vfd_decoder->ioctl_ops	= &s5p_jpeg_ioctl_ops;  	jpeg->vfd_decoder->minor	= -1;  	jpeg->vfd_decoder->release	= video_device_release;  	jpeg->vfd_decoder->lock		= &jpeg->lock;  	jpeg->vfd_decoder->v4l2_dev	= &jpeg->v4l2_dev; +	jpeg->vfd_decoder->vfl_dir	= VFL_DIR_M2M;  	ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);  	if (ret) { @@ -1463,7 +2056,6 @@ device_register_rollback:  	v4l2_device_unregister(&jpeg->v4l2_dev);  clk_get_rollback: -	clk_disable_unprepare(jpeg->clk);  	clk_put(jpeg->clk);  	return ret; @@ -1483,7 +2075,9 @@ static int s5p_jpeg_remove(struct platform_device *pdev)  	v4l2_m2m_release(jpeg->m2m_dev);  	v4l2_device_unregister(&jpeg->v4l2_dev); -	clk_disable_unprepare(jpeg->clk); +	if (!pm_runtime_status_suspended(&pdev->dev)) +		clk_disable_unprepare(jpeg->clk); +  	clk_put(jpeg->clk);  	return 0; @@ -1491,41 +2085,124 @@ static int s5p_jpeg_remove(struct platform_device *pdev)  static int s5p_jpeg_runtime_suspend(struct device *dev)  { +	struct s5p_jpeg *jpeg = dev_get_drvdata(dev); + +	clk_disable_unprepare(jpeg->clk); +  	return 0;  }  static int s5p_jpeg_runtime_resume(struct device *dev)  {  	struct s5p_jpeg *jpeg = dev_get_drvdata(dev); +	unsigned long flags; +	int ret; + +	ret = clk_prepare_enable(jpeg->clk); +	if (ret < 0) +		return ret; + +	spin_lock_irqsave(&jpeg->slock, flags); +  	/*  	 * JPEG IP allows storing two Huffman tables for each component -	 * We fill table 0 for each component +	 * We fill table 0 for each component and do this here only +	 * for S5PC210 device as Exynos4x12 requires programming its +	 * Huffman tables each time the encoding process is initialized.  	 */ -	jpeg_set_hdctbl(jpeg->regs); -	jpeg_set_hdctblg(jpeg->regs); -	jpeg_set_hactbl(jpeg->regs); -	jpeg_set_hactblg(jpeg->regs); +	if (jpeg->variant->version == SJPEG_S5P) { +		s5p_jpeg_set_hdctbl(jpeg->regs); +		s5p_jpeg_set_hdctblg(jpeg->regs); +		s5p_jpeg_set_hactbl(jpeg->regs); +		s5p_jpeg_set_hactblg(jpeg->regs); +	} + +	spin_unlock_irqrestore(&jpeg->slock, flags); +  	return 0;  } +static int s5p_jpeg_suspend(struct device *dev) +{ +	if (pm_runtime_suspended(dev)) +		return 0; + +	return s5p_jpeg_runtime_suspend(dev); +} + +static int s5p_jpeg_resume(struct device *dev) +{ +	if (pm_runtime_suspended(dev)) +		return 0; + +	return s5p_jpeg_runtime_resume(dev); +} +  static const struct dev_pm_ops s5p_jpeg_pm_ops = { -	.runtime_suspend = s5p_jpeg_runtime_suspend, -	.runtime_resume	 = s5p_jpeg_runtime_resume, +	SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume) +	SET_RUNTIME_PM_OPS(s5p_jpeg_runtime_suspend, s5p_jpeg_runtime_resume, NULL) +}; + +static struct s5p_jpeg_variant s5p_jpeg_drvdata = { +	.version	= SJPEG_S5P, +	.jpeg_irq	= s5p_jpeg_irq, +	.m2m_ops	= &s5p_jpeg_m2m_ops, +	.fmt_ver_flag	= SJPEG_FMT_FLAG_S5P,  }; +static struct s5p_jpeg_variant exynos4_jpeg_drvdata = { +	.version	= SJPEG_EXYNOS4, +	.jpeg_irq	= exynos4_jpeg_irq, +	.m2m_ops	= &exynos4_jpeg_m2m_ops, +	.fmt_ver_flag	= SJPEG_FMT_FLAG_EXYNOS4, +}; + +static const struct of_device_id samsung_jpeg_match[] = { +	{ +		.compatible = "samsung,s5pv210-jpeg", +		.data = &s5p_jpeg_drvdata, +	}, { +		.compatible = "samsung,exynos4210-jpeg", +		.data = &s5p_jpeg_drvdata, +	}, { +		.compatible = "samsung,exynos4212-jpeg", +		.data = &exynos4_jpeg_drvdata, +	}, +	{}, +}; + +MODULE_DEVICE_TABLE(of, samsung_jpeg_match); + +static void *jpeg_get_drv_data(struct device *dev) +{ +	struct s5p_jpeg_variant *driver_data = NULL; +	const struct of_device_id *match; + +	if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) +		return &s5p_jpeg_drvdata; + +	match = of_match_node(samsung_jpeg_match, dev->of_node); + +	if (match) +		driver_data = (struct s5p_jpeg_variant *)match->data; + +	return driver_data; +} +  static struct platform_driver s5p_jpeg_driver = {  	.probe = s5p_jpeg_probe,  	.remove = s5p_jpeg_remove,  	.driver = { -		.owner = THIS_MODULE, -		.name = S5P_JPEG_M2M_NAME, -		.pm = &s5p_jpeg_pm_ops, +		.of_match_table	= of_match_ptr(samsung_jpeg_match), +		.owner		= THIS_MODULE, +		.name		= S5P_JPEG_M2M_NAME, +		.pm		= &s5p_jpeg_pm_ops,  	},  };  module_platform_driver(s5p_jpeg_driver);  MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>"); +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");  MODULE_DESCRIPTION("Samsung JPEG codec driver");  MODULE_LICENSE("GPL"); - diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 8a4013e3aee..3e478632972 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -13,6 +13,7 @@  #ifndef JPEG_CORE_H_  #define JPEG_CORE_H_ +#include <linux/interrupt.h>  #include <media/v4l2-device.h>  #include <media/v4l2-fh.h>  #include <media/v4l2-ctrls.h> @@ -43,8 +44,45 @@  #define DHP				0xde  /* Flags that indicate a format can be used for capture/output */ -#define MEM2MEM_CAPTURE			(1 << 0) -#define MEM2MEM_OUTPUT			(1 << 1) +#define SJPEG_FMT_FLAG_ENC_CAPTURE	(1 << 0) +#define SJPEG_FMT_FLAG_ENC_OUTPUT	(1 << 1) +#define SJPEG_FMT_FLAG_DEC_CAPTURE	(1 << 2) +#define SJPEG_FMT_FLAG_DEC_OUTPUT	(1 << 3) +#define SJPEG_FMT_FLAG_S5P		(1 << 4) +#define SJPEG_FMT_FLAG_EXYNOS4		(1 << 5) +#define SJPEG_FMT_RGB			(1 << 6) +#define SJPEG_FMT_NON_RGB		(1 << 7) + +#define S5P_JPEG_ENCODE		0 +#define S5P_JPEG_DECODE		1 + +#define FMT_TYPE_OUTPUT		0 +#define FMT_TYPE_CAPTURE	1 + +#define SJPEG_SUBSAMPLING_444	0x11 +#define SJPEG_SUBSAMPLING_422	0x21 +#define SJPEG_SUBSAMPLING_420	0x22 + +/* Version numbers */ + +#define SJPEG_S5P	1 +#define SJPEG_EXYNOS4	2 + +enum exynos4_jpeg_result { +	OK_ENC_OR_DEC, +	ERR_PROT, +	ERR_DEC_INVALID_FORMAT, +	ERR_MULTI_SCAN, +	ERR_FRAME, +	ERR_UNKNOWN, +}; + +enum  exynos4_jpeg_img_quality_level { +	QUALITY_LEVEL_1 = 0,	/* high */ +	QUALITY_LEVEL_2, +	QUALITY_LEVEL_3, +	QUALITY_LEVEL_4,	/* low */ +};  /**   * struct s5p_jpeg - JPEG IP abstraction @@ -71,9 +109,18 @@ struct s5p_jpeg {  	void __iomem		*regs;  	unsigned int		irq; +	enum exynos4_jpeg_result irq_ret;  	struct clk		*clk;  	struct device		*dev;  	void			*alloc_ctx; +	struct s5p_jpeg_variant *variant; +}; + +struct s5p_jpeg_variant { +	unsigned int		version; +	unsigned int		fmt_ver_flag; +	struct v4l2_m2m_ops	*m2m_ops; +	irqreturn_t		(*jpeg_irq)(int irq, void *priv);  };  /** @@ -84,16 +131,18 @@ struct s5p_jpeg {   * @colplanes:	number of color planes (1 for packed formats)   * @h_align:	horizontal alignment order (align to 2^h_align)   * @v_align:	vertical alignment order (align to 2^v_align) - * @types:	types of queue this format is applicable to + * @flags:	flags describing format applicability   */  struct s5p_jpeg_fmt {  	char	*name;  	u32	fourcc;  	int	depth;  	int	colplanes; +	int	memplanes;  	int	h_align;  	int	v_align; -	u32	types; +	int	subsampling; +	u32	flags;  };  /** @@ -115,7 +164,6 @@ struct s5p_jpeg_q_data {   * @jpeg:		JPEG IP device for this context   * @mode:		compression (encode) operation or decompression (decode)   * @compr_quality:	destination image quality in compression (encode) mode - * @m2m_ctx:		mem2mem device context   * @out_q:		source (output) queue information   * @cap_fmt:		destination (capture) queue queue information   * @hdr_parsed:		set if header has been parsed during decompression @@ -127,7 +175,6 @@ struct s5p_jpeg_ctx {  	unsigned short		compr_quality;  	unsigned short		restart_interval;  	unsigned short		subsampling; -	struct v4l2_m2m_ctx	*m2m_ctx;  	struct s5p_jpeg_q_data	out_q;  	struct s5p_jpeg_q_data	cap_q;  	struct v4l2_fh		fh; @@ -147,4 +194,16 @@ struct s5p_jpeg_buffer {  	unsigned long data;  }; +/** + * struct s5p_jpeg_addr - JPEG converter physical address set for DMA + * @y:   luminance plane physical address + * @cb:  Cb plane physical address + * @cr:  Cr plane physical address + */ +struct s5p_jpeg_addr { +	u32     y; +	u32     cb; +	u32     cr; +}; +  #endif /* JPEG_CORE_H */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c new file mode 100644 index 00000000000..da8d6a1a984 --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c @@ -0,0 +1,279 @@ +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + *		http://www.samsung.com/ + * + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + * + * Register interface file for JPEG driver on Exynos4x12. + * + * 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. + */ +#include <linux/io.h> +#include <linux/delay.h> + +#include "jpeg-core.h" +#include "jpeg-hw-exynos4.h" +#include "jpeg-regs.h" + +void exynos4_jpeg_sw_reset(void __iomem *base) +{ +	unsigned int reg; + +	reg = readl(base + EXYNOS4_JPEG_CNTL_REG); +	writel(reg & ~EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); + +	ndelay(100000); + +	writel(reg | EXYNOS4_SOFT_RESET_HI, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode) +{ +	unsigned int reg; + +	reg = readl(base + EXYNOS4_JPEG_CNTL_REG); +	/* set exynos4_jpeg mod register */ +	if (mode == S5P_JPEG_DECODE) { +		writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | +					EXYNOS4_DEC_MODE, +			base + EXYNOS4_JPEG_CNTL_REG); +	} else {/* encode */ +		writel((reg & EXYNOS4_ENC_DEC_MODE_MASK) | +					EXYNOS4_ENC_MODE, +			base + EXYNOS4_JPEG_CNTL_REG); +	} +} + +void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt) +{ +	unsigned int reg; + +	reg = readl(base + EXYNOS4_IMG_FMT_REG) & +			EXYNOS4_ENC_IN_FMT_MASK; /* clear except enc format */ + +	switch (img_fmt) { +	case V4L2_PIX_FMT_GREY: +		reg = reg | EXYNOS4_ENC_GRAY_IMG | EXYNOS4_GRAY_IMG_IP; +		break; +	case V4L2_PIX_FMT_RGB32: +		reg = reg | EXYNOS4_ENC_RGB_IMG | +				EXYNOS4_RGB_IP_RGB_32BIT_IMG; +		break; +	case V4L2_PIX_FMT_RGB565: +		reg = reg | EXYNOS4_ENC_RGB_IMG | +				EXYNOS4_RGB_IP_RGB_16BIT_IMG; +		break; +	case V4L2_PIX_FMT_NV24: +		reg = reg | EXYNOS4_ENC_YUV_444_IMG | +				EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CBCR; +		break; +	case V4L2_PIX_FMT_NV42: +		reg = reg | EXYNOS4_ENC_YUV_444_IMG | +				EXYNOS4_YUV_444_IP_YUV_444_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CRCB; +		break; +	case V4L2_PIX_FMT_YUYV: +		reg = reg | EXYNOS4_DEC_YUV_422_IMG | +				EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | +				EXYNOS4_SWAP_CHROMA_CBCR; +		break; + +	case V4L2_PIX_FMT_YVYU: +		reg = reg | EXYNOS4_DEC_YUV_422_IMG | +				EXYNOS4_YUV_422_IP_YUV_422_1P_IMG | +				EXYNOS4_SWAP_CHROMA_CRCB; +		break; +	case V4L2_PIX_FMT_NV16: +		reg = reg | EXYNOS4_DEC_YUV_422_IMG | +				EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CBCR; +		break; +	case V4L2_PIX_FMT_NV61: +		reg = reg | EXYNOS4_DEC_YUV_422_IMG | +				EXYNOS4_YUV_422_IP_YUV_422_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CRCB; +		break; +	case V4L2_PIX_FMT_NV12: +		reg = reg | EXYNOS4_DEC_YUV_420_IMG | +				EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CBCR; +		break; +	case V4L2_PIX_FMT_NV21: +		reg = reg | EXYNOS4_DEC_YUV_420_IMG | +				EXYNOS4_YUV_420_IP_YUV_420_2P_IMG | +				EXYNOS4_SWAP_CHROMA_CRCB; +		break; +	case V4L2_PIX_FMT_YUV420: +		reg = reg | EXYNOS4_DEC_YUV_420_IMG | +				EXYNOS4_YUV_420_IP_YUV_420_3P_IMG | +				EXYNOS4_SWAP_CHROMA_CBCR; +		break; +	default: +		break; + +	} + +	writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt) +{ +	unsigned int reg; + +	reg = readl(base + EXYNOS4_IMG_FMT_REG) & +			~EXYNOS4_ENC_FMT_MASK; /* clear enc format */ + +	switch (out_fmt) { +	case V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY: +		reg = reg | EXYNOS4_ENC_FMT_GRAY; +		break; + +	case V4L2_JPEG_CHROMA_SUBSAMPLING_444: +		reg = reg | EXYNOS4_ENC_FMT_YUV_444; +		break; + +	case V4L2_JPEG_CHROMA_SUBSAMPLING_422: +		reg = reg | EXYNOS4_ENC_FMT_YUV_422; +		break; + +	case V4L2_JPEG_CHROMA_SUBSAMPLING_420: +		reg = reg | EXYNOS4_ENC_FMT_YUV_420; +		break; + +	default: +		break; +	} + +	writel(reg, base + EXYNOS4_IMG_FMT_REG); +} + +void exynos4_jpeg_set_interrupt(void __iomem *base) +{ +	unsigned int reg; + +	reg = readl(base + EXYNOS4_INT_EN_REG) & ~EXYNOS4_INT_EN_MASK; +	writel(EXYNOS4_INT_EN_ALL, base + EXYNOS4_INT_EN_REG); +} + +unsigned int exynos4_jpeg_get_int_status(void __iomem *base) +{ +	unsigned int	int_status; + +	int_status = readl(base + EXYNOS4_INT_STATUS_REG); + +	return int_status; +} + +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base) +{ +	unsigned int fifo_status; + +	fifo_status = readl(base + EXYNOS4_FIFO_STATUS_REG); + +	return fifo_status; +} + +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value) +{ +	unsigned int	reg; + +	reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~EXYNOS4_HUF_TBL_EN; + +	if (value == 1) +		writel(reg | EXYNOS4_HUF_TBL_EN, +					base + EXYNOS4_JPEG_CNTL_REG); +	else +		writel(reg | ~EXYNOS4_HUF_TBL_EN, +					base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value) +{ +	unsigned int	reg; + +	reg = readl(base + EXYNOS4_JPEG_CNTL_REG) & ~(EXYNOS4_SYS_INT_EN); + +	if (value == 1) +		writel(EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); +	else +		writel(~EXYNOS4_SYS_INT_EN, base + EXYNOS4_JPEG_CNTL_REG); +} + +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, +					 unsigned int address) +{ +	writel(address, base + EXYNOS4_OUT_MEM_BASE_REG); +} + +void exynos4_jpeg_set_stream_size(void __iomem *base, +		unsigned int x_value, unsigned int y_value) +{ +	writel(0x0, base + EXYNOS4_JPEG_IMG_SIZE_REG); /* clear */ +	writel(EXYNOS4_X_SIZE(x_value) | EXYNOS4_Y_SIZE(y_value), +			base + EXYNOS4_JPEG_IMG_SIZE_REG); +} + +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, +				struct s5p_jpeg_addr *exynos4_jpeg_addr) +{ +	writel(exynos4_jpeg_addr->y, base + EXYNOS4_IMG_BA_PLANE_1_REG); +	writel(exynos4_jpeg_addr->cb, base + EXYNOS4_IMG_BA_PLANE_2_REG); +	writel(exynos4_jpeg_addr->cr, base + EXYNOS4_IMG_BA_PLANE_3_REG); +} + +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, +		enum exynos4_jpeg_img_quality_level level) +{ +	unsigned int	reg; + +	reg = EXYNOS4_Q_TBL_COMP1_0 | EXYNOS4_Q_TBL_COMP2_1 | +		EXYNOS4_Q_TBL_COMP3_1 | +		EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1 | +		EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0 | +		EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1; + +	writel(reg, base + EXYNOS4_TBL_SEL_REG); +} + +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt) +{ +	if (fmt == V4L2_PIX_FMT_GREY) +		writel(0xd2, base + EXYNOS4_HUFF_CNT_REG); +	else +		writel(0x1a2, base + EXYNOS4_HUFF_CNT_REG); +} + +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base) +{ +	unsigned int size; + +	size = readl(base + EXYNOS4_BITSTREAM_SIZE_REG); +	return size; +} + +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size) +{ +	writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG); +} + +void exynos4_jpeg_get_frame_size(void __iomem *base, +			unsigned int *width, unsigned int *height) +{ +	*width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) & +				EXYNOS4_DECODED_SIZE_MASK); +	*height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) & +				EXYNOS4_DECODED_SIZE_MASK; +} + +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base) +{ +	return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) & +				EXYNOS4_JPEG_DECODED_IMG_FMT_MASK; +} + +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size) +{ +	writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG); +} diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h new file mode 100644 index 00000000000..c228d28a4bc --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2013 Samsung Electronics Co., Ltd. + *		http://www.samsung.com/ + * + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + * + * Header file of the register interface for JPEG driver on Exynos4x12. + * + * 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. +*/ + +#ifndef JPEG_HW_EXYNOS4_H_ +#define JPEG_HW_EXYNOS4_H_ + +void exynos4_jpeg_sw_reset(void __iomem *base); +void exynos4_jpeg_set_enc_dec_mode(void __iomem *base, unsigned int mode); +void exynos4_jpeg_set_img_fmt(void __iomem *base, unsigned int img_fmt); +void exynos4_jpeg_set_enc_out_fmt(void __iomem *base, unsigned int out_fmt); +void exynos4_jpeg_set_enc_tbl(void __iomem *base); +void exynos4_jpeg_set_interrupt(void __iomem *base); +unsigned int exynos4_jpeg_get_int_status(void __iomem *base); +void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value); +void exynos4_jpeg_set_sys_int_enable(void __iomem *base, int value); +void exynos4_jpeg_set_stream_buf_address(void __iomem *base, +					 unsigned int address); +void exynos4_jpeg_set_stream_size(void __iomem *base, +		unsigned int x_value, unsigned int y_value); +void exynos4_jpeg_set_frame_buf_address(void __iomem *base, +				struct s5p_jpeg_addr *jpeg_addr); +void exynos4_jpeg_set_encode_tbl_select(void __iomem *base, +		enum exynos4_jpeg_img_quality_level level); +void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt); +void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size); +unsigned int exynos4_jpeg_get_stream_size(void __iomem *base); +void exynos4_jpeg_get_frame_size(void __iomem *base, +			unsigned int *width, unsigned int *height); +unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base); +unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base); +void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size); + +#endif /* JPEG_HW_EXYNOS4_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c index b47e887b613..52407d79072 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c @@ -9,27 +9,15 @@   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ -#ifndef JPEG_HW_H_ -#define JPEG_HW_H_  #include <linux/io.h>  #include <linux/videodev2.h> -#include "jpeg-hw.h" +#include "jpeg-core.h"  #include "jpeg-regs.h" +#include "jpeg-hw-s5p.h" -#define S5P_JPEG_MIN_WIDTH		32 -#define S5P_JPEG_MIN_HEIGHT		32 -#define S5P_JPEG_MAX_WIDTH		8192 -#define S5P_JPEG_MAX_HEIGHT		8192 -#define S5P_JPEG_ENCODE			0 -#define S5P_JPEG_DECODE			1 -#define S5P_JPEG_RAW_IN_565		0 -#define S5P_JPEG_RAW_IN_422		1 -#define S5P_JPEG_RAW_OUT_422		0 -#define S5P_JPEG_RAW_OUT_420		1 - -static inline void jpeg_reset(void __iomem *regs) +void s5p_jpeg_reset(void __iomem *regs)  {  	unsigned long reg; @@ -42,12 +30,12 @@ static inline void jpeg_reset(void __iomem *regs)  	}  } -static inline void jpeg_poweron(void __iomem *regs) +void s5p_jpeg_poweron(void __iomem *regs)  {  	writel(S5P_POWER_ON, regs + S5P_JPGCLKCON);  } -static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode) +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)  {  	unsigned long reg, m; @@ -63,7 +51,7 @@ static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)  	writel(reg, regs + S5P_JPGCMOD);  } -static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16) +void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16)  {  	unsigned long reg; @@ -75,7 +63,7 @@ static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)  	writel(reg, regs + S5P_JPGCMOD);  } -static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode) +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode)  {  	unsigned long reg, m; @@ -90,7 +78,7 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)  	writel(reg, regs + S5P_JPGMOD);  } -static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode) +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)  {  	unsigned long reg, m; @@ -105,12 +93,12 @@ static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)  	writel(reg, regs + S5P_JPGMOD);  } -static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs) +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs)  {  	return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;  } -static inline void jpeg_dri(void __iomem *regs, unsigned int dri) +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri)  {  	unsigned long reg; @@ -125,7 +113,7 @@ static inline void jpeg_dri(void __iomem *regs, unsigned int dri)  	writel(reg, regs + S5P_JPGDRI_L);  } -static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n) +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)  {  	unsigned long reg; @@ -135,7 +123,7 @@ static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)  	writel(reg, regs + S5P_JPG_QTBL);  } -static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t) +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t)  {  	unsigned long reg; @@ -146,7 +134,7 @@ static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)  	writel(reg, regs + S5P_JPG_HTBL);  } -static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t) +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t)  {  	unsigned long reg; @@ -157,7 +145,7 @@ static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)  	writel(reg, regs + S5P_JPG_HTBL);  } -static inline void jpeg_y(void __iomem *regs, unsigned int y) +void s5p_jpeg_y(void __iomem *regs, unsigned int y)  {  	unsigned long reg; @@ -172,7 +160,7 @@ static inline void jpeg_y(void __iomem *regs, unsigned int y)  	writel(reg, regs + S5P_JPGY_L);  } -static inline void jpeg_x(void __iomem *regs, unsigned int x) +void s5p_jpeg_x(void __iomem *regs, unsigned int x)  {  	unsigned long reg; @@ -187,7 +175,7 @@ static inline void jpeg_x(void __iomem *regs, unsigned int x)  	writel(reg, regs + S5P_JPGX_L);  } -static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable) +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable)  {  	unsigned long reg; @@ -198,7 +186,7 @@ static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)  	writel(reg, regs + S5P_JPGINTSE);  } -static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable) +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable)  {  	unsigned long reg; @@ -209,7 +197,7 @@ static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)  	writel(reg, regs + S5P_JPGINTSE);  } -static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl) +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)  {  	unsigned long reg; @@ -220,7 +208,7 @@ static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)  	writel(reg, regs + S5P_JPGINTSE);  } -static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val) +void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val)  {  	unsigned long reg; @@ -231,7 +219,7 @@ static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)  	writel(reg, regs + S5P_JPG_TIMER_SE);  } -static inline void jpeg_timer_disable(void __iomem *regs) +void s5p_jpeg_timer_disable(void __iomem *regs)  {  	unsigned long reg; @@ -240,13 +228,13 @@ static inline void jpeg_timer_disable(void __iomem *regs)  	writel(reg, regs + S5P_JPG_TIMER_SE);  } -static inline int jpeg_timer_stat(void __iomem *regs) +int s5p_jpeg_timer_stat(void __iomem *regs)  {  	return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)  		     >> S5P_TIMER_INT_STAT_SHIFT);  } -static inline void jpeg_clear_timer_stat(void __iomem *regs) +void s5p_jpeg_clear_timer_stat(void __iomem *regs)  {  	unsigned long reg; @@ -255,7 +243,7 @@ static inline void jpeg_clear_timer_stat(void __iomem *regs)  	writel(reg, regs + S5P_JPG_TIMER_SE);  } -static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size) +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size)  {  	unsigned long reg; @@ -266,13 +254,13 @@ static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)  	writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);  } -static inline int jpeg_enc_stream_stat(void __iomem *regs) +int s5p_jpeg_enc_stream_stat(void __iomem *regs)  {  	return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) &  		     S5P_ENC_STREAM_INT_STAT_MASK);  } -static inline void jpeg_clear_enc_stream_stat(void __iomem *regs) +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs)  {  	unsigned long reg; @@ -281,7 +269,7 @@ static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)  	writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);  } -static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format) +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format)  {  	unsigned long reg, f; @@ -296,17 +284,17 @@ static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)  	writel(reg, regs + S5P_JPG_OUTFORM);  } -static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr) +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr)  {  	writel(addr, regs + S5P_JPG_JPGADR);  } -static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr) +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr)  {  	writel(addr, regs + S5P_JPG_IMGADR);  } -static inline void jpeg_coef(void __iomem *regs, unsigned int i, +void s5p_jpeg_coef(void __iomem *regs, unsigned int i,  			     unsigned int j, unsigned int coef)  {  	unsigned long reg; @@ -317,24 +305,24 @@ static inline void jpeg_coef(void __iomem *regs, unsigned int i,  	writel(reg, regs + S5P_JPG_COEF(i));  } -static inline void jpeg_start(void __iomem *regs) +void s5p_jpeg_start(void __iomem *regs)  {  	writel(1, regs + S5P_JSTART);  } -static inline int jpeg_result_stat_ok(void __iomem *regs) +int s5p_jpeg_result_stat_ok(void __iomem *regs)  {  	return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK)  		     >> S5P_RESULT_STAT_SHIFT);  } -static inline int jpeg_stream_stat_ok(void __iomem *regs) +int s5p_jpeg_stream_stat_ok(void __iomem *regs)  {  	return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK)  		      >> S5P_STREAM_STAT_SHIFT);  } -static inline void jpeg_clear_int(void __iomem *regs) +void s5p_jpeg_clear_int(void __iomem *regs)  {  	unsigned long reg; @@ -343,7 +331,7 @@ static inline void jpeg_clear_int(void __iomem *regs)  	reg = readl(regs + S5P_JPGOPR);  } -static inline unsigned int jpeg_compressed_size(void __iomem *regs) +unsigned int s5p_jpeg_compressed_size(void __iomem *regs)  {  	unsigned long jpeg_size = 0; @@ -353,5 +341,3 @@ static inline unsigned int jpeg_compressed_size(void __iomem *regs)  	return (unsigned int)jpeg_size;  } - -#endif /* JPEG_HW_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h new file mode 100644 index 00000000000..c11ebe86b9c --- /dev/null +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h @@ -0,0 +1,63 @@ +/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + *		http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * 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. + */ +#ifndef JPEG_HW_S5P_H_ +#define JPEG_HW_S5P_H_ + +#include <linux/io.h> +#include <linux/videodev2.h> + +#include "jpeg-regs.h" + +#define S5P_JPEG_MIN_WIDTH		32 +#define S5P_JPEG_MIN_HEIGHT		32 +#define S5P_JPEG_MAX_WIDTH		8192 +#define S5P_JPEG_MAX_HEIGHT		8192 +#define S5P_JPEG_RAW_IN_565		0 +#define S5P_JPEG_RAW_IN_422		1 +#define S5P_JPEG_RAW_OUT_422		0 +#define S5P_JPEG_RAW_OUT_420		1 + +void s5p_jpeg_reset(void __iomem *regs); +void s5p_jpeg_poweron(void __iomem *regs); +void s5p_jpeg_input_raw_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_input_raw_y16(void __iomem *regs, bool y16); +void s5p_jpeg_proc_mode(void __iomem *regs, unsigned long mode); +void s5p_jpeg_subsampling_mode(void __iomem *regs, unsigned int mode); +unsigned int s5p_jpeg_get_subsampling_mode(void __iomem *regs); +void s5p_jpeg_dri(void __iomem *regs, unsigned int dri); +void s5p_jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n); +void s5p_jpeg_htbl_ac(void __iomem *regs, unsigned int t); +void s5p_jpeg_htbl_dc(void __iomem *regs, unsigned int t); +void s5p_jpeg_y(void __iomem *regs, unsigned int y); +void s5p_jpeg_x(void __iomem *regs, unsigned int x); +void s5p_jpeg_rst_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_data_num_int_enable(void __iomem *regs, bool enable); +void s5p_jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl); +void s5p_jpeg_timer_enable(void __iomem *regs, unsigned long val); +void s5p_jpeg_timer_disable(void __iomem *regs); +int s5p_jpeg_timer_stat(void __iomem *regs); +void s5p_jpeg_clear_timer_stat(void __iomem *regs); +void s5p_jpeg_enc_stream_int(void __iomem *regs, unsigned long size); +int s5p_jpeg_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_clear_enc_stream_stat(void __iomem *regs); +void s5p_jpeg_outform_raw(void __iomem *regs, unsigned long format); +void s5p_jpeg_jpgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_imgadr(void __iomem *regs, unsigned long addr); +void s5p_jpeg_coef(void __iomem *regs, unsigned int i, +			     unsigned int j, unsigned int coef); +void s5p_jpeg_start(void __iomem *regs); +int s5p_jpeg_result_stat_ok(void __iomem *regs); +int s5p_jpeg_stream_stat_ok(void __iomem *regs); +void s5p_jpeg_clear_int(void __iomem *regs); +unsigned int s5p_jpeg_compressed_size(void __iomem *regs); + +#endif /* JPEG_HW_S5P_H_ */ diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 38e50815668..57fb05bb8c7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h @@ -2,10 +2,11 @@   *   * Register definition file for Samsung JPEG codec driver   * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2013 Samsung Electronics Co., Ltd.   *		http://www.samsung.com   *   * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * Author: Jacek Anaszewski <j.anaszewski@samsung.com>   *   * 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 @@ -15,6 +16,8 @@  #ifndef JPEG_REGS_H_  #define JPEG_REGS_H_ +/* Register and bit definitions for S5PC210 */ +  /* JPEG mode register */  #define S5P_JPGMOD			0x00  #define S5P_PROC_MODE_MASK		(0x1 << 3) @@ -166,5 +169,209 @@  /* JPEG AC Huffman table register */  #define S5P_JPG_HACTBLG(n)		(0x8c0 + (n) * 0x400) + +/* Register and bit definitions for Exynos 4x12 */ + +/* JPEG Codec Control Registers */ +#define EXYNOS4_JPEG_CNTL_REG		0x00 +#define EXYNOS4_INT_EN_REG		0x04 +#define EXYNOS4_INT_TIMER_COUNT_REG	0x08 +#define EXYNOS4_INT_STATUS_REG		0x0c +#define EXYNOS4_OUT_MEM_BASE_REG		0x10 +#define EXYNOS4_JPEG_IMG_SIZE_REG	0x14 +#define EXYNOS4_IMG_BA_PLANE_1_REG	0x18 +#define EXYNOS4_IMG_SO_PLANE_1_REG	0x1c +#define EXYNOS4_IMG_PO_PLANE_1_REG	0x20 +#define EXYNOS4_IMG_BA_PLANE_2_REG	0x24 +#define EXYNOS4_IMG_SO_PLANE_2_REG	0x28 +#define EXYNOS4_IMG_PO_PLANE_2_REG	0x2c +#define EXYNOS4_IMG_BA_PLANE_3_REG	0x30 +#define EXYNOS4_IMG_SO_PLANE_3_REG	0x34 +#define EXYNOS4_IMG_PO_PLANE_3_REG	0x38 + +#define EXYNOS4_TBL_SEL_REG		0x3c + +#define EXYNOS4_IMG_FMT_REG		0x40 + +#define EXYNOS4_BITSTREAM_SIZE_REG	0x44 +#define EXYNOS4_PADDING_REG		0x48 +#define EXYNOS4_HUFF_CNT_REG		0x4c +#define EXYNOS4_FIFO_STATUS_REG	0x50 +#define EXYNOS4_DECODE_XY_SIZE_REG	0x54 +#define EXYNOS4_DECODE_IMG_FMT_REG	0x58 + +#define EXYNOS4_QUAN_TBL_ENTRY_REG	0x100 +#define EXYNOS4_HUFF_TBL_ENTRY_REG	0x200 + + +/****************************************************************/ +/* Bit definition part						*/ +/****************************************************************/ + +/* JPEG CNTL Register bit */ +#define EXYNOS4_ENC_DEC_MODE_MASK	(0xfffffffc << 0) +#define EXYNOS4_DEC_MODE		(1 << 0) +#define EXYNOS4_ENC_MODE		(1 << 1) +#define EXYNOS4_AUTO_RST_MARKER		(1 << 2) +#define EXYNOS4_RST_INTERVAL_SHIFT	3 +#define EXYNOS4_RST_INTERVAL(x)		(((x) & 0xffff) \ +						<< EXYNOS4_RST_INTERVAL_SHIFT) +#define EXYNOS4_HUF_TBL_EN		(1 << 19) +#define EXYNOS4_HOR_SCALING_SHIFT	20 +#define EXYNOS4_HOR_SCALING_MASK	(3 << EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_HOR_SCALING(x)		(((x) & 0x3) \ +						<< EXYNOS4_HOR_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING_SHIFT	22 +#define EXYNOS4_VER_SCALING_MASK	(3 << EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_VER_SCALING(x)		(((x) & 0x3) \ +						<< EXYNOS4_VER_SCALING_SHIFT) +#define EXYNOS4_PADDING			(1 << 27) +#define EXYNOS4_SYS_INT_EN		(1 << 28) +#define EXYNOS4_SOFT_RESET_HI		(1 << 29) + +/* JPEG INT Register bit */ +#define EXYNOS4_INT_EN_MASK		(0x1f << 0) +#define EXYNOS4_PROT_ERR_INT_EN		(1 << 0) +#define EXYNOS4_IMG_COMPLETION_INT_EN	(1 << 1) +#define EXYNOS4_DEC_INVALID_FORMAT_EN	(1 << 2) +#define EXYNOS4_MULTI_SCAN_ERROR_EN	(1 << 3) +#define EXYNOS4_FRAME_ERR_EN		(1 << 4) +#define EXYNOS4_INT_EN_ALL		(0x1f << 0) + +#define EXYNOS4_MOD_REG_PROC_ENC	(0 << 3) +#define EXYNOS4_MOD_REG_PROC_DEC	(1 << 3) + +#define EXYNOS4_MOD_REG_SUBSAMPLE_444	(0 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_422	(1 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_420	(2 << 0) +#define EXYNOS4_MOD_REG_SUBSAMPLE_GRAY	(3 << 0) + + +/* JPEG IMAGE SIZE Register bit */ +#define EXYNOS4_X_SIZE_SHIFT		0 +#define EXYNOS4_X_SIZE_MASK		(0xffff << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_X_SIZE(x)		(((x) & 0xffff) << EXYNOS4_X_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE_SHIFT		16 +#define EXYNOS4_Y_SIZE_MASK		(0xffff << EXYNOS4_Y_SIZE_SHIFT) +#define EXYNOS4_Y_SIZE(x)		(((x) & 0xffff) << EXYNOS4_Y_SIZE_SHIFT) + +/* JPEG IMAGE FORMAT Register bit */ +#define EXYNOS4_ENC_IN_FMT_MASK		0xffff0000 +#define EXYNOS4_ENC_GRAY_IMG		(0 << 0) +#define EXYNOS4_ENC_RGB_IMG		(1 << 0) +#define EXYNOS4_ENC_YUV_444_IMG		(2 << 0) +#define EXYNOS4_ENC_YUV_422_IMG		(3 << 0) +#define EXYNOS4_ENC_YUV_440_IMG		(4 << 0) + +#define EXYNOS4_DEC_GRAY_IMG		(0 << 0) +#define EXYNOS4_DEC_RGB_IMG		(1 << 0) +#define EXYNOS4_DEC_YUV_444_IMG		(2 << 0) +#define EXYNOS4_DEC_YUV_422_IMG		(3 << 0) +#define EXYNOS4_DEC_YUV_420_IMG		(4 << 0) + +#define EXYNOS4_GRAY_IMG_IP_SHIFT	3 +#define EXYNOS4_GRAY_IMG_IP_MASK	(7 << EXYNOS4_GRAY_IMG_IP_SHIFT) +#define EXYNOS4_GRAY_IMG_IP		(4 << EXYNOS4_GRAY_IMG_IP_SHIFT) + +#define EXYNOS4_RGB_IP_SHIFT		6 +#define EXYNOS4_RGB_IP_MASK		(7 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_16BIT_IMG	(4 << EXYNOS4_RGB_IP_SHIFT) +#define EXYNOS4_RGB_IP_RGB_32BIT_IMG	(5 << EXYNOS4_RGB_IP_SHIFT) + +#define EXYNOS4_YUV_444_IP_SHIFT		9 +#define EXYNOS4_YUV_444_IP_MASK			(7 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG	(4 << EXYNOS4_YUV_444_IP_SHIFT) +#define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG	(5 << EXYNOS4_YUV_444_IP_SHIFT) + +#define EXYNOS4_YUV_422_IP_SHIFT		12 +#define EXYNOS4_YUV_422_IP_MASK			(7 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG	(4 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG	(5 << EXYNOS4_YUV_422_IP_SHIFT) +#define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG	(6 << EXYNOS4_YUV_422_IP_SHIFT) + +#define EXYNOS4_YUV_420_IP_SHIFT		15 +#define EXYNOS4_YUV_420_IP_MASK			(7 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG	(4 << EXYNOS4_YUV_420_IP_SHIFT) +#define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG	(5 << EXYNOS4_YUV_420_IP_SHIFT) + +#define EXYNOS4_ENC_FMT_SHIFT			24 +#define EXYNOS4_ENC_FMT_MASK			(3 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_GRAY			(0 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_444			(1 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_422			(2 << EXYNOS4_ENC_FMT_SHIFT) +#define EXYNOS4_ENC_FMT_YUV_420			(3 << EXYNOS4_ENC_FMT_SHIFT) + +#define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK	0x03 + +#define EXYNOS4_SWAP_CHROMA_CRCB		(1 << 26) +#define EXYNOS4_SWAP_CHROMA_CBCR		(0 << 26) + +/* JPEG HUFF count Register bit */ +#define EXYNOS4_HUFF_COUNT_MASK			0xffff + +/* JPEG Decoded_img_x_y_size Register bit */ +#define EXYNOS4_DECODED_SIZE_MASK		0x0000ffff + +/* JPEG Decoded image format Register bit */ +#define EXYNOS4_DECODED_IMG_FMT_MASK		0x3 + +/* JPEG TBL SEL Register bit */ +#define EXYNOS4_Q_TBL_COMP1_0		(0 << 0) +#define EXYNOS4_Q_TBL_COMP1_1		(1 << 0) +#define EXYNOS4_Q_TBL_COMP1_2		(2 << 0) +#define EXYNOS4_Q_TBL_COMP1_3		(3 << 0) + +#define EXYNOS4_Q_TBL_COMP2_0		(0 << 2) +#define EXYNOS4_Q_TBL_COMP2_1		(1 << 2) +#define EXYNOS4_Q_TBL_COMP2_2		(2 << 2) +#define EXYNOS4_Q_TBL_COMP2_3		(3 << 2) + +#define EXYNOS4_Q_TBL_COMP3_0		(0 << 4) +#define EXYNOS4_Q_TBL_COMP3_1		(1 << 4) +#define EXYNOS4_Q_TBL_COMP3_2		(2 << 4) +#define EXYNOS4_Q_TBL_COMP3_3		(3 << 4) + +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_0	(0 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_0_DC_1	(1 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_0	(2 << 6) +#define EXYNOS4_HUFF_TBL_COMP1_AC_1_DC_1	(3 << 6) + +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_0	(0 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_0_DC_1	(1 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_0	(2 << 8) +#define EXYNOS4_HUFF_TBL_COMP2_AC_1_DC_1	(3 << 8) + +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_0	(0 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_0_DC_1	(1 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_0	(2 << 10) +#define EXYNOS4_HUFF_TBL_COMP3_AC_1_DC_1	(3 << 10) + +/* JPEG quantizer table register */ +#define EXYNOS4_QTBL_CONTENT(n)	(0x100 + (n) * 0x40) + +/* JPEG DC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLL	0x200 + +/* JPEG DC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCLV	0x210 + +/* JPEG DC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCL	0x220 + +/* JPEG DC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HDCCV	0x230 + +/* JPEG AC luminance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLL	0x240 + +/* JPEG AC luminance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACLV	0x250 + +/* JPEG AC chrominance (code length) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCL	0x300 + +/* JPEG AC chrominance (values) Huffman table register */ +#define EXYNOS4_HUFF_TBL_HACCV	0x310 +  #endif /* JPEG_REGS_H_ */ diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h index 2398cdf6134..51cb2dd0e13 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h @@ -141,6 +141,7 @@  #define S5P_FIMV_D_SLICE_IF_ENABLE_V6		0xf4c4  #define S5P_FIMV_D_PICTURE_TAG_V6		0xf4c8  #define S5P_FIMV_D_STREAM_DATA_SIZE_V6		0xf4d0 +#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6	0xf47c  /* Display information register */  #define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6	0xf500 @@ -229,6 +230,7 @@  #define S5P_FIMV_E_PADDING_CTRL_V6		0xf7a4  #define S5P_FIMV_E_MV_HOR_RANGE_V6		0xf7ac  #define S5P_FIMV_E_MV_VER_RANGE_V6		0xf7b0 +#define S5P_FIMV_E_MV_RANGE_V6_MASK		0x3fff  #define S5P_FIMV_E_VBV_BUFFER_SIZE_V6		0xf84c  #define S5P_FIMV_E_VBV_INIT_DELAY_V6		0xf850 @@ -380,8 +382,7 @@  	 (DIV_ROUND_UP((mbw) * (mbh), 32) * 16))  #define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h)	(((w) * 192) + 64)  #define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \ -			((w) * ((h) * 64 + 144) + (2048/16 * (h) * 64) + \ -			 (2048/16 * 256 + 8320)) +			((w) * 144 + 8192 * (h) + 49216 + 1048576)  #define S5P_FIMV_SCRATCH_BUF_SIZE_VC1_DEC_V6(w, h) \  						(2096 * ((w) + (h) + 1))  #define S5P_FIMV_SCRATCH_BUF_SIZE_H263_DEC_V6(w, h)	((w) * 400) diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h index ea5ec2a711a..1a5c6fdf784 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h @@ -18,8 +18,6 @@  #define S5P_FIMV_CODEC_VP8_ENC_V7	25  /* Additional registers for v7 */ -#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7		0xf47c -  #define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7			0xf9e0  #define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7		0xf9e4  #define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7			0xf9e8 @@ -56,6 +54,7 @@  			(SZ_1M + ((w) * 144) + (8192 * (h)) + 49216)  #define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \ -			(((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192) +			(((w) * 48) + 8192 + ((((w) + 1) / 2) * 128) + 144 + \ +			((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4))  #endif /*_REGS_MFC_V7_H*/ diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h new file mode 100644 index 00000000000..cc7cbec51b5 --- /dev/null +++ b/drivers/media/platform/s5p-mfc/regs-mfc-v8.h @@ -0,0 +1,124 @@ +/* + * Register definition file for Samsung MFC V8.x Interface (FIMV) driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + *		http://www.samsung.com/ + * + * 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. + */ + +#ifndef _REGS_MFC_V8_H +#define _REGS_MFC_V8_H + +#include <linux/sizes.h> +#include "regs-mfc-v7.h" + +/* Additional registers for v8 */ +#define S5P_FIMV_D_MVC_NUM_VIEWS_V8		0xf104 +#define S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8	0xf144 +#define S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8	0xf148 +#define S5P_FIMV_D_MV_BUFFER_SIZE_V8		0xf150 + +#define S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8	0xf138 +#define S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8	0xf13c + +#define S5P_FIMV_D_FIRST_PLANE_DPB_V8		0xf160 +#define S5P_FIMV_D_SECOND_PLANE_DPB_V8		0xf260 +#define S5P_FIMV_D_MV_BUFFER_V8			0xf460 + +#define S5P_FIMV_D_NUM_MV_V8			0xf134 +#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8	0xf154 + +#define S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8	0xf560 +#define S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8	0xf564 + +#define S5P_FIMV_D_CPB_BUFFER_ADDR_V8		0xf5b0 +#define S5P_FIMV_D_CPB_BUFFER_SIZE_V8		0xf5b4 +#define S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8	0xf5bc +#define S5P_FIMV_D_CPB_BUFFER_OFFSET_V8		0xf5c0 +#define S5P_FIMV_D_SLICE_IF_ENABLE_V8		0xf5c4 +#define S5P_FIMV_D_STREAM_DATA_SIZE_V8		0xf5d0 + +/* Display information register */ +#define S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8	0xf600 +#define S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8	0xf604 + +/* Display status */ +#define S5P_FIMV_D_DISPLAY_STATUS_V8		0xf608 + +#define S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8	0xf60c +#define S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8	0xf610 + +#define S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8	0xf618 +#define S5P_FIMV_D_DISPLAY_CROP_INFO1_V8	0xf61c +#define S5P_FIMV_D_DISPLAY_CROP_INFO2_V8	0xf620 +#define S5P_FIMV_D_DISPLAY_PICTURE_PROFILE_V8	0xf624 + +/* Decoded picture information register */ +#define S5P_FIMV_D_DECODED_STATUS_V8		0xf644 +#define S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8	0xf648 +#define S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8	0xf64c +#define S5P_FIMV_D_DECODED_THIRD_PLANE_ADDR_V8	0xf650 +#define S5P_FIMV_D_DECODED_FRAME_TYPE_V8	0xf654 +#define S5P_FIMV_D_DECODED_NAL_SIZE_V8          0xf664 + +/* Returned value register for specific setting */ +#define S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8	0xf674 +#define S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8	0xf678 +#define S5P_FIMV_D_MVC_VIEW_ID_V8		0xf6d8 + +/* SEI related information */ +#define S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8	0xf6dc + +/* Encoder Registers */ +#define S5P_FIMV_E_FIXED_PICTURE_QP_V8		0xf794 +#define S5P_FIMV_E_RC_CONFIG_V8			0xf798 +#define S5P_FIMV_E_RC_QP_BOUND_V8		0xf79c +#define S5P_FIMV_E_RC_RPARAM_V8			0xf7a4 +#define S5P_FIMV_E_MB_RC_CONFIG_V8		0xf7a8 +#define S5P_FIMV_E_PADDING_CTRL_V8		0xf7ac +#define S5P_FIMV_E_MV_HOR_RANGE_V8		0xf7b4 +#define S5P_FIMV_E_MV_VER_RANGE_V8		0xf7b8 + +#define S5P_FIMV_E_VBV_BUFFER_SIZE_V8		0xf78c +#define S5P_FIMV_E_VBV_INIT_DELAY_V8		0xf790 + +#define S5P_FIMV_E_ASPECT_RATIO_V8		0xfb4c +#define S5P_FIMV_E_EXTENDED_SAR_V8		0xfb50 +#define S5P_FIMV_E_H264_OPTIONS_V8		0xfb54 + +/* MFCv8 Context buffer sizes */ +#define MFC_CTX_BUF_SIZE_V8		(30 * SZ_1K)	/*  30KB */ +#define MFC_H264_DEC_CTX_BUF_SIZE_V8	(2 * SZ_1M)	/*  2MB */ +#define MFC_OTHER_DEC_CTX_BUF_SIZE_V8	(20 * SZ_1K)	/*  20KB */ +#define MFC_H264_ENC_CTX_BUF_SIZE_V8	(100 * SZ_1K)	/* 100KB */ +#define MFC_OTHER_ENC_CTX_BUF_SIZE_V8	(10 * SZ_1K)	/*  10KB */ + +/* Buffer size defines */ +#define S5P_FIMV_TMV_BUFFER_SIZE_V8(w, h)	(((w) + 1) * ((h) + 1) * 8) + +#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8(w, h)	(((w) * 704) + 2176) +#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8(w, h) \ +		(((w) * 576 + (h) * 128)  + 4128) + +#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(w, h) \ +			(((w) * 592) + 2336) +#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(w, h) \ +			(((w) * 576) + 10512 + \ +			((((((w) * 16) * ((h) * 16)) * 3) / 2) * 4)) +#define S5P_FIMV_ME_BUFFER_SIZE_V8(imw, imh, mbw, mbh) \ +	((DIV_ROUND_UP((mbw * 16), 64) *  DIV_ROUND_UP((mbh * 16), 64) * 256) \ +	 + (DIV_ROUND_UP((mbw) * (mbh), 32) * 16)) + +/* BUffer alignment defines */ +#define S5P_FIMV_D_ALIGN_PLANE_SIZE_V8	64 + +/* MFCv8 variant defines */ +#define MAX_FW_SIZE_V8			(SZ_1M)		/* 1MB */ +#define MAX_CPB_SIZE_V8			(3 * SZ_1M)	/* 3MB */ +#define MFC_VERSION_V8			0x80 +#define MFC_NUM_PORTS_V8		1 + +#endif /*_REGS_MFC_V8_H*/ diff --git a/drivers/media/platform/s5p-mfc/regs-mfc.h b/drivers/media/platform/s5p-mfc/regs-mfc.h index 9319e93599a..6ccc3f8c122 100644 --- a/drivers/media/platform/s5p-mfc/regs-mfc.h +++ b/drivers/media/platform/s5p-mfc/regs-mfc.h @@ -382,7 +382,7 @@  #define S5P_FIMV_R2H_CMD_EDFU_INIT_RET		16  #define S5P_FIMV_R2H_CMD_ERR_RET		32 -/* Dummy definition for MFCv6 compatibilty */ +/* Dummy definition for MFCv6 compatibility */  #define S5P_FIMV_CODEC_H264_MVC_DEC		-1  #define S5P_FIMV_R2H_CMD_FIELD_DONE_RET		-1  #define S5P_FIMV_MFC_RESET			-1 diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 084263dd126..41723180d10 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -177,21 +177,6 @@ unlock:  		mutex_unlock(&dev->mfc_mutex);  } -static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file) -{ -	struct video_device *vdev = video_devdata(file); - -	if (!vdev) { -		mfc_err("failed to get video_device"); -		return MFCNODE_INVALID; -	} -	if (vdev->index == 0) -		return MFCNODE_DECODER; -	else if (vdev->index == 1) -		return MFCNODE_ENCODER; -	return MFCNODE_INVALID; -} -  static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)  {  	mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT); @@ -239,7 +224,7 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)  	frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);  	/* Copy timestamp / timecode from decoded src to dst and set -	   appropraite flags */ +	   appropriate flags */  	src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);  	list_for_each_entry(dst_buf, &ctx->dst_queue, list) {  		if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) { @@ -247,6 +232,11 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)  						src_buf->b->v4l2_buf.timecode;  			dst_buf->b->v4l2_buf.timestamp =  						src_buf->b->v4l2_buf.timestamp; +			dst_buf->b->v4l2_buf.flags &= +				~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; +			dst_buf->b->v4l2_buf.flags |= +				src_buf->b->v4l2_buf.flags +				& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;  			switch (frame_type) {  			case S5P_FIMV_DECODE_FRAME_I_FRAME:  				dst_buf->b->v4l2_buf.flags |= @@ -319,12 +309,15 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,  {  	struct s5p_mfc_dev *dev = ctx->dev;  	unsigned int dst_frame_status; +	unsigned int dec_frame_status;  	struct s5p_mfc_buf *src_buf;  	unsigned long flags;  	unsigned int res_change;  	dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)  				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; +	dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev) +				& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;  	res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)  				& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)  				>> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT; @@ -349,16 +342,23 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,  	/* All frames remaining in the buffer have been extracted  */  	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {  		if (ctx->state == MFCINST_RES_CHANGE_FLUSH) { +			static const struct v4l2_event ev_src_ch = { +				.type = V4L2_EVENT_SOURCE_CHANGE, +				.u.src_change.changes = +					V4L2_EVENT_SRC_CH_RESOLUTION, +			}; +  			s5p_mfc_handle_frame_all_extracted(ctx);  			ctx->state = MFCINST_RES_CHANGE_END; +			v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); +  			goto leave_handle_frame;  		} else {  			s5p_mfc_handle_frame_all_extracted(ctx);  		}  	} -	if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY || -		dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY) +	if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)  		s5p_mfc_handle_frame_copy_time(ctx);  	/* A frame has been decoded and is in the buffer  */ @@ -376,6 +376,7 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,  		ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,  						get_consumed_stream, dev);  		if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC && +			ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&  			ctx->consumed_stream + STUFF_BYTE <  			src_buf->b->v4l2_planes[0].bytesused) {  			/* Run MFC again on the same buffer */ @@ -404,7 +405,11 @@ leave_handle_frame:  	if (test_and_clear_bit(0, &dev->hw_lock) == 0)  		BUG();  	s5p_mfc_clock_off(); -	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); +	/* if suspending, wake up device and do not try_run again*/ +	if (test_bit(0, &dev->enter_suspend)) +		wake_up_dev(dev, reason, err); +	else +		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);  }  /* Error handling for interrupt */ @@ -424,7 +429,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,  		case MFCINST_FINISHING:  		case MFCINST_FINISHED:  		case MFCINST_RUNNING: -			/* It is higly probable that an error occured +			/* It is highly probable that an error occurred  			 * while decoding a frame */  			clear_work_bit(ctx);  			ctx->state = MFCINST_ERROR; @@ -607,7 +612,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)  	mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);  	switch (reason) {  	case S5P_MFC_R2H_CMD_ERR_RET: -		/* An error has occured */ +		/* An error has occurred */  		if (ctx->state == MFCINST_RUNNING &&  			s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=  				dev->warn_start) @@ -647,6 +652,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)  	case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:  		clear_work_bit(ctx); +		ctx->inst_no = MFC_NO_INSTANCE_SET;  		ctx->state = MFCINST_FREE;  		wake_up(&ctx->queue);  		goto irq_cleanup_hw; @@ -701,6 +707,7 @@ irq_cleanup_hw:  /* Open an MFC node */  static int s5p_mfc_open(struct file *file)  { +	struct video_device *vdev = video_devdata(file);  	struct s5p_mfc_dev *dev = video_drvdata(file);  	struct s5p_mfc_ctx *ctx = NULL;  	struct vb2_queue *q; @@ -738,7 +745,7 @@ static int s5p_mfc_open(struct file *file)  	/* Mark context as idle */  	clear_work_bit_irqsave(ctx);  	dev->ctx[ctx->num] = ctx; -	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { +	if (vdev == dev->vfd_dec) {  		ctx->type = MFCINST_DECODER;  		ctx->c_ops = get_dec_codec_ops();  		s5p_mfc_dec_init(ctx); @@ -748,7 +755,7 @@ static int s5p_mfc_open(struct file *file)  			mfc_err("Failed to setup mfc controls\n");  			goto err_ctrls_setup;  		} -	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { +	} else if (vdev == dev->vfd_enc) {  		ctx->type = MFCINST_ENCODER;  		ctx->c_ops = get_enc_codec_ops();  		/* only for encoder */ @@ -766,7 +773,7 @@ static int s5p_mfc_open(struct file *file)  		goto err_bad_node;  	}  	ctx->fh.ctrl_handler = &ctx->ctrl_handler; -	ctx->inst_no = -1; +	ctx->inst_no = MFC_NO_INSTANCE_SET;  	/* Load firmware if this is the first instance */  	if (dev->num_inst == 1) {  		dev->watchdog_timer.expires = jiffies + @@ -793,10 +800,10 @@ static int s5p_mfc_open(struct file *file)  	q = &ctx->vq_dst;  	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;  	q->drv_priv = &ctx->fh; -	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { +	if (vdev == dev->vfd_dec) {  		q->io_modes = VB2_MMAP;  		q->ops = get_dec_queue_ops(); -	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { +	} else if (vdev == dev->vfd_enc) {  		q->io_modes = VB2_MMAP | VB2_USERPTR;  		q->ops = get_enc_queue_ops();  	} else { @@ -804,7 +811,7 @@ static int s5p_mfc_open(struct file *file)  		goto err_queue_init;  	}  	q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(q);  	if (ret) {  		mfc_err("Failed to initialize videobuf2 queue(capture)\n"); @@ -815,10 +822,10 @@ static int s5p_mfc_open(struct file *file)  	q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;  	q->io_modes = VB2_MMAP;  	q->drv_priv = &ctx->fh; -	if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) { +	if (vdev == dev->vfd_dec) {  		q->io_modes = VB2_MMAP;  		q->ops = get_dec_queue_ops(); -	} else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) { +	} else if (vdev == dev->vfd_enc) {  		q->io_modes = VB2_MMAP | VB2_USERPTR;  		q->ops = get_enc_queue_ops();  	} else { @@ -826,7 +833,7 @@ static int s5p_mfc_open(struct file *file)  		goto err_queue_init;  	}  	q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(q);  	if (ret) {  		mfc_err("Failed to initialize videobuf2 queue(output)\n"); @@ -836,7 +843,7 @@ static int s5p_mfc_open(struct file *file)  	mutex_unlock(&dev->mfc_mutex);  	mfc_debug_leave();  	return ret; -	/* Deinit when failure occured */ +	/* Deinit when failure occurred */  err_queue_init:  	if (dev->num_inst == 1)  		s5p_mfc_deinit_hw(dev); @@ -876,29 +883,11 @@ static int s5p_mfc_release(struct file *file)  	vb2_queue_release(&ctx->vq_dst);  	/* Mark context as idle */  	clear_work_bit_irqsave(ctx); -	/* If instance was initialised then -	 * return instance and free reosurces */ -	if (ctx->inst_no != MFC_NO_INSTANCE_SET) { +	/* If instance was initialised and not yet freed, +	 * return instance and free resources */ +	if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) {  		mfc_debug(2, "Has to free instance\n"); -		ctx->state = MFCINST_RETURN_INST; -		set_work_bit_irqsave(ctx); -		s5p_mfc_clean_ctx_int_flags(ctx); -		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); -		/* Wait until instance is returned or timeout occured */ -		if (s5p_mfc_wait_for_done_ctx -		    (ctx, S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) { -			s5p_mfc_clock_off(); -			mfc_err("Err returning instance\n"); -		} -		mfc_debug(2, "After free instance\n"); -		/* Free resources */ -		s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); -		s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); -		if (ctx->type == MFCINST_DECODER) -			s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, -					ctx); - -		ctx->inst_no = MFC_NO_INSTANCE_SET; +		s5p_mfc_close_mfc_inst(dev, ctx);  	}  	/* hardware locking scheme */  	if (dev->curr_ctx == ctx->num) @@ -1101,7 +1090,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)  	}  	dev->irq = res->start;  	ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, -					IRQF_DISABLED, pdev->name, dev); +					0, pdev->name, dev);  	if (ret) {  		dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);  		goto err_res; @@ -1157,9 +1146,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)  		ret = -ENOMEM;  		goto err_dec_alloc;  	} -	vfd->fops	= &s5p_mfc_fops, +	vfd->fops	= &s5p_mfc_fops;  	vfd->ioctl_ops	= get_dec_v4l2_ioctl_ops(); -	vfd->release	= video_device_release, +	vfd->release	= video_device_release;  	vfd->lock	= &dev->mfc_mutex;  	vfd->v4l2_dev	= &dev->v4l2_dev;  	vfd->vfl_dir	= VFL_DIR_M2M; @@ -1182,9 +1171,9 @@ static int s5p_mfc_probe(struct platform_device *pdev)  		ret = -ENOMEM;  		goto err_enc_alloc;  	} -	vfd->fops	= &s5p_mfc_fops, +	vfd->fops	= &s5p_mfc_fops;  	vfd->ioctl_ops	= get_enc_v4l2_ioctl_ops(); -	vfd->release	= video_device_release, +	vfd->release	= video_device_release;  	vfd->lock	= &dev->mfc_mutex;  	vfd->v4l2_dev	= &dev->v4l2_dev;  	vfd->vfl_dir	= VFL_DIR_M2M; @@ -1212,6 +1201,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)  	/* Initialize HW ops and commands based on MFC version */  	s5p_mfc_init_hw_ops(dev);  	s5p_mfc_init_hw_cmds(dev); +	s5p_mfc_init_regs(dev);  	pr_debug("%s--\n", __func__);  	return 0; @@ -1286,9 +1276,7 @@ static int s5p_mfc_suspend(struct device *dev)  		/* Try and lock the HW */  		/* Wait on the interrupt waitqueue */  		ret = wait_event_interruptible_timeout(m_dev->queue, -			m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond, -			msecs_to_jiffies(MFC_INT_TIMEOUT)); - +			m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));  		if (ret == 0) {  			mfc_err("Waiting for hardware to finish timed out\n");  			return -EIO; @@ -1359,6 +1347,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v5 = {  static struct s5p_mfc_variant mfc_drvdata_v5 = {  	.version	= MFC_VERSION, +	.version_bit	= MFC_V5_BIT,  	.port_num	= MFC_NUM_PORTS,  	.buf_size	= &buf_size_v5,  	.buf_align	= &mfc_buf_align_v5, @@ -1385,6 +1374,7 @@ struct s5p_mfc_buf_align mfc_buf_align_v6 = {  static struct s5p_mfc_variant mfc_drvdata_v6 = {  	.version	= MFC_VERSION_V6, +	.version_bit	= MFC_V6_BIT,  	.port_num	= MFC_NUM_PORTS_V6,  	.buf_size	= &buf_size_v6,  	.buf_align	= &mfc_buf_align_v6, @@ -1411,12 +1401,40 @@ struct s5p_mfc_buf_align mfc_buf_align_v7 = {  static struct s5p_mfc_variant mfc_drvdata_v7 = {  	.version	= MFC_VERSION_V7, +	.version_bit	= MFC_V7_BIT,  	.port_num	= MFC_NUM_PORTS_V7,  	.buf_size	= &buf_size_v7,  	.buf_align	= &mfc_buf_align_v7,  	.fw_name        = "s5p-mfc-v7.fw",  }; +struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { +	.dev_ctx	= MFC_CTX_BUF_SIZE_V8, +	.h264_dec_ctx	= MFC_H264_DEC_CTX_BUF_SIZE_V8, +	.other_dec_ctx	= MFC_OTHER_DEC_CTX_BUF_SIZE_V8, +	.h264_enc_ctx	= MFC_H264_ENC_CTX_BUF_SIZE_V8, +	.other_enc_ctx	= MFC_OTHER_ENC_CTX_BUF_SIZE_V8, +}; + +struct s5p_mfc_buf_size buf_size_v8 = { +	.fw	= MAX_FW_SIZE_V8, +	.cpb	= MAX_CPB_SIZE_V8, +	.priv	= &mfc_buf_size_v8, +}; + +struct s5p_mfc_buf_align mfc_buf_align_v8 = { +	.base = 0, +}; + +static struct s5p_mfc_variant mfc_drvdata_v8 = { +	.version	= MFC_VERSION_V8, +	.version_bit	= MFC_V8_BIT, +	.port_num	= MFC_NUM_PORTS_V8, +	.buf_size	= &buf_size_v8, +	.buf_align	= &mfc_buf_align_v8, +	.fw_name        = "s5p-mfc-v8.fw", +}; +  static struct platform_device_id mfc_driver_ids[] = {  	{  		.name = "s5p-mfc", @@ -1430,6 +1448,9 @@ static struct platform_device_id mfc_driver_ids[] = {  	}, {  		.name = "s5p-mfc-v7",  		.driver_data = (unsigned long)&mfc_drvdata_v7, +	}, { +		.name = "s5p-mfc-v8", +		.driver_data = (unsigned long)&mfc_drvdata_v8,  	},  	{},  }; @@ -1445,6 +1466,9 @@ static const struct of_device_id exynos_mfc_match[] = {  	}, {  		.compatible = "samsung,mfc-v7",  		.data = &mfc_drvdata_v7, +	}, { +		.compatible = "samsung,mfc-v8", +		.data = &mfc_drvdata_v8,  	},  	{},  }; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c index ad4f1df0a18..9a6efd6c132 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c @@ -111,7 +111,7 @@ static int s5p_mfc_open_inst_cmd_v5(struct s5p_mfc_ctx *ctx)  		break;  	default:  		h2r_args.arg[0] = S5P_FIMV_CODEC_NONE; -	}; +	}  	h2r_args.arg[1] = 0; /* no crc & no pixelcache */  	h2r_args.arg[2] = ctx->ctx.ofs;  	h2r_args.arg[3] = ctx->ctx.size; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c index db796c8e787..ec1a5947ed7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c @@ -113,7 +113,7 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)  		break;  	default:  		codec_type = S5P_FIMV_CODEC_NONE_V6; -	}; +	}  	mfc_write(dev, codec_type, S5P_FIMV_CODEC_TYPE_V6);  	mfc_write(dev, ctx->ctx.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6);  	mfc_write(dev, ctx->ctx.size, S5P_FIMV_CONTEXT_MEM_SIZE_V6); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 6920b546181..b04360cd34f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -23,8 +23,7 @@  #include <media/v4l2-ioctl.h>  #include <media/videobuf2-core.h>  #include "regs-mfc.h" -#include "regs-mfc-v6.h" -#include "regs-mfc-v7.h" +#include "regs-mfc-v8.h"  /* Definitions related to MFC memory */ @@ -115,15 +114,6 @@ enum s5p_mfc_fmt_type {  };  /** - * enum s5p_mfc_node_type - The type of an MFC device node. - */ -enum s5p_mfc_node_type { -	MFCNODE_INVALID = -1, -	MFCNODE_DECODER = 0, -	MFCNODE_ENCODER = 1, -}; - -/**   * enum s5p_mfc_inst_type - The type of an MFC instance.   */  enum s5p_mfc_inst_type { @@ -232,6 +222,7 @@ struct s5p_mfc_buf_align {  struct s5p_mfc_variant {  	unsigned int version;  	unsigned int port_num; +	u32 version_bit;  	struct s5p_mfc_buf_size *buf_size;  	struct s5p_mfc_buf_align *buf_align;  	char	*fw_name; @@ -339,6 +330,7 @@ struct s5p_mfc_dev {  	int warn_start;  	struct s5p_mfc_hw_ops *mfc_ops;  	struct s5p_mfc_hw_cmds *mfc_cmds; +	const struct s5p_mfc_regs *mfc_regs;  };  /** @@ -422,6 +414,11 @@ struct s5p_mfc_vp8_enc_params {  	enum v4l2_vp8_golden_frame_sel golden_frame_sel;  	u8 hier_layer;  	u8 hier_layer_qp[3]; +	u8 rc_min_qp; +	u8 rc_max_qp; +	u8 rc_frame_qp; +	u8 rc_p_frame_qp; +	u8 profile;  };  /** @@ -430,6 +427,8 @@ struct s5p_mfc_vp8_enc_params {  struct s5p_mfc_enc_params {  	u16 width;  	u16 height; +	u32 mv_h_range; +	u32 mv_v_range;  	u16 gop_size;  	enum v4l2_mpeg_video_multi_slice_mode slice_mode; @@ -665,6 +664,7 @@ struct s5p_mfc_fmt {  	u32 codec_mode;  	enum s5p_mfc_fmt_type type;  	u32 num_planes; +	u32 versions;  };  /** @@ -702,6 +702,13 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);  				(dev->variant->port_num ? 1 : 0) : 0) : 0)  #define IS_TWOPORT(dev)		(dev->variant->port_num == 2 ? 1 : 0)  #define IS_MFCV6_PLUS(dev)	(dev->variant->version >= 0x60 ? 1 : 0) -#define IS_MFCV7(dev)		(dev->variant->version >= 0x70 ? 1 : 0) +#define IS_MFCV7_PLUS(dev)	(dev->variant->version >= 0x70 ? 1 : 0) +#define IS_MFCV8(dev)		(dev->variant->version >= 0x80 ? 1 : 0) + +#define MFC_V5_BIT	BIT(0) +#define MFC_V6_BIT	BIT(1) +#define MFC_V7_BIT	BIT(2) +#define MFC_V8_BIT	BIT(3) +  #endif /* S5P_MFC_COMMON_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 7cab6849fb5..6c3f8f74390 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -44,8 +44,6 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)  		return -ENOMEM;  	} -	dev->bank1 = dev->bank1; -  	if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) {  		bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER,  					&bank2_dma_addr, GFP_KERNEL); @@ -69,7 +67,7 @@ int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev)  	} else {  		/* In this case bank2 can point to the same address as bank1. -		 * Firmware will always occupy the beggining of this area so it is +		 * Firmware will always occupy the beginning of this area so it is  		 * impossible having a video frame buffer with zero address. */  		dev->bank2 = dev->bank1;  	} @@ -402,3 +400,65 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)  	return 0;  } +int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) +{ +	int ret = 0; + +	ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx); +	if (ret) { +		mfc_err("Failed allocating instance buffer\n"); +		goto err; +	} + +	if (ctx->type == MFCINST_DECODER) { +		ret = s5p_mfc_hw_call(dev->mfc_ops, +					alloc_dec_temp_buffers, ctx); +		if (ret) { +			mfc_err("Failed allocating temporary buffers\n"); +			goto err_free_inst_buf; +		} +	} + +	set_work_bit_irqsave(ctx); +	s5p_mfc_clean_ctx_int_flags(ctx); +	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); +	if (s5p_mfc_wait_for_done_ctx(ctx, +		S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) { +		/* Error or timeout */ +		mfc_err("Error getting instance from hardware\n"); +		ret = -EIO; +		goto err_free_desc_buf; +	} + +	mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); +	return ret; + +err_free_desc_buf: +	if (ctx->type == MFCINST_DECODER) +		s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); +err_free_inst_buf: +	s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); +err: +	return ret; +} + +void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx) +{ +	ctx->state = MFCINST_RETURN_INST; +	set_work_bit_irqsave(ctx); +	s5p_mfc_clean_ctx_int_flags(ctx); +	s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); +	/* Wait until instance is returned or timeout occurred */ +	if (s5p_mfc_wait_for_done_ctx(ctx, +				S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0)) +		mfc_err("Err returning instance\n"); + +	/* Free resources */ +	s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); +	s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx); +	if (ctx->type == MFCINST_DECODER) +		s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx); + +	ctx->inst_no = MFC_NO_INSTANCE_SET; +	ctx->state = MFCINST_FREE; +} diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h index 6a9b6f8606b..8e5df041edf 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h @@ -28,4 +28,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);  int s5p_mfc_reset(struct s5p_mfc_dev *dev); +int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx); +void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx); +  #endif /* S5P_MFC_CTRL_H */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 8faf9691712..4d93835dec9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -25,15 +25,13 @@  #include <media/v4l2-event.h>  #include <media/videobuf2-core.h>  #include "s5p_mfc_common.h" +#include "s5p_mfc_ctrl.h"  #include "s5p_mfc_debug.h"  #include "s5p_mfc_dec.h"  #include "s5p_mfc_intr.h"  #include "s5p_mfc_opr.h"  #include "s5p_mfc_pm.h" -#define DEF_SRC_FMT_DEC	V4L2_PIX_FMT_H264 -#define DEF_DST_FMT_DEC	V4L2_PIX_FMT_NV12MT_16X16 -  static struct s5p_mfc_fmt formats[] = {  	{  		.name		= "4:2:0 2 Planes 16x16 Tiles", @@ -41,6 +39,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V6_BIT | MFC_V7_BIT,  	},  	{  		.name		= "4:2:0 2 Planes 64x32 Tiles", @@ -48,6 +47,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V5_BIT,  	},  	{  		.name		= "4:2:0 2 Planes Y/CbCr", @@ -55,6 +55,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,  	},  	{  		.name		= "4:2:0 2 Planes Y/CrCb", @@ -62,6 +63,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,  	},  	{  		.name		= "H264 Encoded Stream", @@ -69,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_H264_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "H264/MVC Encoded Stream", @@ -76,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_H264_MVC_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,  	},  	{  		.name		= "H263 Encoded Stream", @@ -83,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_H263_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "MPEG1 Encoded Stream", @@ -90,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_MPEG2_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "MPEG2 Encoded Stream", @@ -97,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_MPEG2_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "MPEG4 Encoded Stream", @@ -104,6 +115,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_MPEG4_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "XviD Encoded Stream", @@ -111,6 +124,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_MPEG4_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "VC1 Encoded Stream", @@ -118,6 +133,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_VC1_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "VC1 RCV Encoded Stream", @@ -125,6 +142,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_VC1RCV_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "VP8 Encoded Stream", @@ -132,6 +151,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_VP8_DEC,  		.type		= MFC_FMT_DEC,  		.num_planes	= 1, +		.versions	= MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,  	},  }; @@ -262,8 +282,10 @@ static int vidioc_querycap(struct file *file, void *priv,  }  /* Enumerate format */ -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) +static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, +							bool mplane, bool out)  { +	struct s5p_mfc_dev *dev = video_drvdata(file);  	struct s5p_mfc_fmt *fmt;  	int i, j = 0; @@ -276,6 +298,8 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)  			continue;  		else if (!out && formats[i].type != MFC_FMT_RAW)  			continue; +		else if ((dev->variant->version_bit & formats[i].versions) == 0) +			continue;  		if (j == f->index)  			break; @@ -292,25 +316,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)  static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,  							struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, false, false); +	return vidioc_enum_fmt(file, f, false, false);  }  static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,  							struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, true, false); +	return vidioc_enum_fmt(file, f, true, false);  } -static int vidioc_enum_fmt_vid_out(struct file *file, void *prov, +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,  							struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, false, true); +	return vidioc_enum_fmt(file, f, false, true);  } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, +static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv,  							struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, true, true); +	return vidioc_enum_fmt(file, f, true, true);  }  /* Get format */ @@ -386,11 +410,9 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)  			mfc_err("Unknown codec\n");  			return -EINVAL;  		} -		if (!IS_MFCV6_PLUS(dev)) { -			if (fmt->fourcc == V4L2_PIX_FMT_VP8) { -				mfc_err("Not supported format.\n"); -				return -EINVAL; -			} +		if ((dev->variant->version_bit & fmt->versions) == 0) { +			mfc_err("Unsupported format by this MFC version.\n"); +			return -EINVAL;  		}  	} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {  		fmt = find_format(f, MFC_FMT_RAW); @@ -398,13 +420,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)  			mfc_err("Unsupported format for destination.\n");  			return -EINVAL;  		} -		if (IS_MFCV6_PLUS(dev) && -				(fmt->fourcc == V4L2_PIX_FMT_NV12MT)) { -			mfc_err("Not supported format.\n"); -			return -EINVAL; -		} else if (!IS_MFCV6_PLUS(dev) && -				(fmt->fourcc != V4L2_PIX_FMT_NV12MT)) { -			mfc_err("Not supported format.\n"); +		if ((dev->variant->version_bit & fmt->versions) == 0) { +			mfc_err("Unsupported format by this MFC version.\n");  			return -EINVAL;  		}  	} @@ -462,104 +479,131 @@ out:  	return ret;  } -/* Reqeust buffers */ -static int vidioc_reqbufs(struct file *file, void *priv, -					  struct v4l2_requestbuffers *reqbufs) +static int reqbufs_output(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, +				struct v4l2_requestbuffers *reqbufs)  { -	struct s5p_mfc_dev *dev = video_drvdata(file); -	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);  	int ret = 0; -	if (reqbufs->memory != V4L2_MEMORY_MMAP) { -		mfc_err("Only V4L2_MEMORY_MAP is supported\n"); -		return -EINVAL; -	} -	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { -		/* Can only request buffers after an instance has been opened.*/ -		if (ctx->state == MFCINST_INIT) { -			ctx->src_bufs_cnt = 0; -			if (reqbufs->count == 0) { -				mfc_debug(2, "Freeing buffers\n"); -				s5p_mfc_clock_on(); -				ret = vb2_reqbufs(&ctx->vq_src, reqbufs); -				s5p_mfc_clock_off(); -				return ret; -			} -			/* Decoding */ -			if (ctx->output_state != QUEUE_FREE) { -				mfc_err("Bufs have already been requested\n"); -				return -EINVAL; -			} -			s5p_mfc_clock_on(); -			ret = vb2_reqbufs(&ctx->vq_src, reqbufs); -			s5p_mfc_clock_off(); -			if (ret) { -				mfc_err("vb2_reqbufs on output failed\n"); -				return ret; -			} -			mfc_debug(2, "vb2_reqbufs: %d\n", ret); -			ctx->output_state = QUEUE_BUFS_REQUESTED; -		} -	} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { -		ctx->dst_bufs_cnt = 0; -		if (reqbufs->count == 0) { -			mfc_debug(2, "Freeing buffers\n"); -			s5p_mfc_clock_on(); -			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); -			s5p_mfc_clock_off(); -			return ret; +	s5p_mfc_clock_on(); + +	if (reqbufs->count == 0) { +		mfc_debug(2, "Freeing buffers\n"); +		ret = vb2_reqbufs(&ctx->vq_src, reqbufs); +		if (ret) +			goto out; +		s5p_mfc_close_mfc_inst(dev, ctx); +		ctx->src_bufs_cnt = 0; +		ctx->output_state = QUEUE_FREE; +	} else if (ctx->output_state == QUEUE_FREE) { +		/* Can only request buffers when we have a valid format set. */ +		WARN_ON(ctx->src_bufs_cnt != 0); +		if (ctx->state != MFCINST_INIT) { +			mfc_err("Reqbufs called in an invalid state\n"); +			ret = -EINVAL; +			goto out;  		} -		if (ctx->capture_state != QUEUE_FREE) { -			mfc_err("Bufs have already been requested\n"); -			return -EINVAL; -		} -		ctx->capture_state = QUEUE_BUFS_REQUESTED; -		s5p_mfc_clock_on(); -		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); -		s5p_mfc_clock_off(); + +		mfc_debug(2, "Allocating %d buffers for OUTPUT queue\n", +				reqbufs->count); +		ret = vb2_reqbufs(&ctx->vq_src, reqbufs); +		if (ret) +			goto out; + +		ret = s5p_mfc_open_mfc_inst(dev, ctx);  		if (ret) { -			mfc_err("vb2_reqbufs on capture failed\n"); -			return ret; -		} -		if (reqbufs->count < ctx->pb_count) { -			mfc_err("Not enough buffers allocated\n");  			reqbufs->count = 0; -			s5p_mfc_clock_on(); -			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); -			s5p_mfc_clock_off(); -			return -ENOMEM; +			vb2_reqbufs(&ctx->vq_src, reqbufs); +			goto out;  		} + +		ctx->output_state = QUEUE_BUFS_REQUESTED; +	} else { +		mfc_err("Buffers have already been requested\n"); +		ret = -EINVAL; +	} +out: +	s5p_mfc_clock_off(); +	if (ret) +		mfc_err("Failed allocating buffers for OUTPUT queue\n"); +	return ret; +} + +static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx, +				struct v4l2_requestbuffers *reqbufs) +{ +	int ret = 0; + +	s5p_mfc_clock_on(); + +	if (reqbufs->count == 0) { +		mfc_debug(2, "Freeing buffers\n"); +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); +		if (ret) +			goto out; +		s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx); +		ctx->dst_bufs_cnt = 0; +	} else if (ctx->capture_state == QUEUE_FREE) { +		WARN_ON(ctx->dst_bufs_cnt != 0); +		mfc_debug(2, "Allocating %d buffers for CAPTURE queue\n", +				reqbufs->count); +		ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); +		if (ret) +			goto out; + +		ctx->capture_state = QUEUE_BUFS_REQUESTED;  		ctx->total_dpb_count = reqbufs->count; +  		ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_codec_buffers, ctx);  		if (ret) {  			mfc_err("Failed to allocate decoding buffers\n");  			reqbufs->count = 0; -			s5p_mfc_clock_on(); -			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); -			s5p_mfc_clock_off(); -			return -ENOMEM; -		} -		if (ctx->dst_bufs_cnt == ctx->total_dpb_count) { -			ctx->capture_state = QUEUE_BUFS_MMAPED; -		} else { -			mfc_err("Not all buffers passed to buf_init\n"); -			reqbufs->count = 0; -			s5p_mfc_clock_on(); -			ret = vb2_reqbufs(&ctx->vq_dst, reqbufs); -			s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, -					ctx); -			s5p_mfc_clock_off(); -			return -ENOMEM; +			vb2_reqbufs(&ctx->vq_dst, reqbufs); +			ret = -ENOMEM; +			ctx->capture_state = QUEUE_FREE; +			goto out;  		} + +		WARN_ON(ctx->dst_bufs_cnt != ctx->total_dpb_count); +		ctx->capture_state = QUEUE_BUFS_MMAPED; +  		if (s5p_mfc_ctx_ready(ctx))  			set_work_bit_irqsave(ctx);  		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); -		s5p_mfc_wait_for_done_ctx(ctx, -					S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, 0); +		s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET, +					  0); +	} else { +		mfc_err("Buffers have already been requested\n"); +		ret = -EINVAL;  	} +out: +	s5p_mfc_clock_off(); +	if (ret) +		mfc_err("Failed allocating buffers for CAPTURE queue\n");  	return ret;  } +/* Reqeust buffers */ +static int vidioc_reqbufs(struct file *file, void *priv, +					  struct v4l2_requestbuffers *reqbufs) +{ +	struct s5p_mfc_dev *dev = video_drvdata(file); +	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); + +	if (reqbufs->memory != V4L2_MEMORY_MMAP) { +		mfc_err("Only V4L2_MEMORY_MAP is supported\n"); +		return -EINVAL; +	} + +	if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { +		return reqbufs_output(dev, ctx, reqbufs); +	} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { +		return reqbufs_capture(dev, ctx, reqbufs); +	} else { +		mfc_err("Invalid type requested\n"); +		return -EINVAL; +	} +} +  /* Query buffer */  static int vidioc_querybuf(struct file *file, void *priv,  						   struct v4l2_buffer *buf) @@ -573,7 +617,7 @@ static int vidioc_querybuf(struct file *file, void *priv,  		return -EINVAL;  	}  	mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type); -	if (ctx->state == MFCINST_INIT && +	if (ctx->state == MFCINST_GOT_INST &&  			buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {  		ret = vb2_querybuf(&ctx->vq_src, buf);  	} else if (ctx->state == MFCINST_RUNNING && @@ -649,39 +693,11 @@ static int vidioc_streamon(struct file *file, void *priv,  			   enum v4l2_buf_type type)  {  	struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); -	struct s5p_mfc_dev *dev = ctx->dev;  	int ret = -EINVAL;  	mfc_debug_enter(); -	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - -		if (ctx->state == MFCINST_INIT) { -			ctx->dst_bufs_cnt = 0; -			ctx->src_bufs_cnt = 0; -			ctx->capture_state = QUEUE_FREE; -			ctx->output_state = QUEUE_FREE; -			s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, -					ctx); -			s5p_mfc_hw_call(dev->mfc_ops, alloc_dec_temp_buffers, -					ctx); -			set_work_bit_irqsave(ctx); -			s5p_mfc_clean_ctx_int_flags(ctx); -			s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - -			if (s5p_mfc_wait_for_done_ctx(ctx, -				S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) { -				/* Error or timeout */ -				mfc_err("Error getting instance from hardware\n"); -				s5p_mfc_hw_call(dev->mfc_ops, -						release_instance_buffer, ctx); -				s5p_mfc_hw_call(dev->mfc_ops, -						release_dec_desc_buffer, ctx); -				return -EIO; -			} -			mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); -		} +	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)  		ret = vb2_streamon(&ctx->vq_src, type); -		}  	else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)  		ret = vb2_streamon(&ctx->vq_dst, type);  	mfc_debug_leave(); @@ -851,6 +867,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh,  	switch (sub->type) {  	case V4L2_EVENT_EOS:  		return v4l2_event_subscribe(fh, sub, 2, NULL); +	case V4L2_EVENT_SOURCE_CHANGE: +		return v4l2_src_change_event_subscribe(fh, sub);  	default:  		return -EINVAL;  	} @@ -1027,7 +1045,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)  	return 0;  } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q)  {  	unsigned long flags;  	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1071,7 +1089,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)  	}  	if (aborted)  		ctx->state = MFCINST_RUNNING; -	return 0;  } @@ -1191,9 +1208,14 @@ void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)  void s5p_mfc_dec_init(struct s5p_mfc_ctx *ctx)  {  	struct v4l2_format f; -	f.fmt.pix_mp.pixelformat = DEF_SRC_FMT_DEC; +	f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;  	ctx->src_fmt = find_format(&f, MFC_FMT_DEC); -	f.fmt.pix_mp.pixelformat = DEF_DST_FMT_DEC; +	if (IS_MFCV8(ctx->dev)) +		f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M; +	else if (IS_MFCV6_PLUS(ctx->dev)) +		f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT_16X16; +	else +		f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12MT;  	ctx->dst_fmt = find_format(&f, MFC_FMT_RAW);  	mfc_debug(2, "Default src_fmt is %x, dest_fmt is %x\n",  			(unsigned int)ctx->src_fmt, (unsigned int)ctx->dst_fmt); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 41f5a3c10db..d26b2484ca1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -26,6 +26,7 @@  #include <media/v4l2-ctrls.h>  #include <media/videobuf2-core.h>  #include "s5p_mfc_common.h" +#include "s5p_mfc_ctrl.h"  #include "s5p_mfc_debug.h"  #include "s5p_mfc_enc.h"  #include "s5p_mfc_intr.h" @@ -41,6 +42,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V6_BIT | MFC_V7_BIT,  	},  	{  		.name		= "4:2:0 2 Planes 64x32 Tiles", @@ -48,6 +50,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V5_BIT,  	},  	{  		.name		= "4:2:0 2 Planes Y/CbCr", @@ -55,6 +58,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "4:2:0 2 Planes Y/CrCb", @@ -62,6 +67,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_NONE,  		.type		= MFC_FMT_RAW,  		.num_planes	= 2, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "H264 Encoded Stream", @@ -69,6 +76,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_H264_ENC,  		.type		= MFC_FMT_ENC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "MPEG4 Encoded Stream", @@ -76,6 +85,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_MPEG4_ENC,  		.type		= MFC_FMT_ENC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "H263 Encoded Stream", @@ -83,6 +94,8 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_H263_ENC,  		.type		= MFC_FMT_ENC,  		.num_planes	= 1, +		.versions	= MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT | +								MFC_V8_BIT,  	},  	{  		.name		= "VP8 Encoded Stream", @@ -90,6 +103,7 @@ static struct s5p_mfc_fmt formats[] = {  		.codec_mode	= S5P_MFC_CODEC_VP8_ENC,  		.type		= MFC_FMT_ENC,  		.num_planes	= 1, +		.versions	= MFC_V7_BIT | MFC_V8_BIT,  	},  }; @@ -113,7 +127,7 @@ static struct mfc_control controls[] = {  		.minimum = 0,  		.maximum = (1 << 16) - 1,  		.step = 1, -		.default_value = 0, +		.default_value = 12,  	},  	{  		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, @@ -208,6 +222,24 @@ static struct mfc_control controls[] = {  		.default_value = 0,  	},  	{ +		.id = V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.name = "Horizontal MV Search Range", +		.minimum = 16, +		.maximum = 128, +		.step = 16, +		.default_value = 32, +	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.name = "Vertical MV Search Range", +		.minimum = 16, +		.maximum = 128, +		.step = 16, +		.default_value = 32, +	}, +	{  		.id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,  		.type = V4L2_CTRL_TYPE_INTEGER,  		.minimum = 0, @@ -356,7 +388,7 @@ static struct mfc_control controls[] = {  		.minimum = 0,  		.maximum = 51,  		.step = 1, -		.default_value = 1, +		.default_value = 51,  	},  	{  		.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, @@ -399,7 +431,7 @@ static struct mfc_control controls[] = {  		.minimum = 1,  		.maximum = 31,  		.step = 1, -		.default_value = 1, +		.default_value = 31,  	},  	{  		.id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP, @@ -444,7 +476,7 @@ static struct mfc_control controls[] = {  		.minimum = 0,  		.maximum = 51,  		.step = 1, -		.default_value = 1, +		.default_value = 51,  	},  	{  		.id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, @@ -618,6 +650,46 @@ static struct mfc_control controls[] = {  		.default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,  		.menu_skip_mask = 0,  	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_VPX_MAX_QP, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.minimum = 0, +		.maximum = 127, +		.step = 1, +		.default_value = 127, +	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_VPX_MIN_QP, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.minimum = 0, +		.maximum = 11, +		.step = 1, +		.default_value = 0, +	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.minimum = 0, +		.maximum = 127, +		.step = 1, +		.default_value = 10, +	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.minimum = 0, +		.maximum = 127, +		.step = 1, +		.default_value = 10, +	}, +	{ +		.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE, +		.type = V4L2_CTRL_TYPE_INTEGER, +		.minimum = 0, +		.maximum = 3, +		.step = 1, +		.default_value = 0, +	},  };  #define NUM_CTRLS ARRAY_SIZE(controls) @@ -714,13 +786,16 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)  	if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {  		spin_lock_irqsave(&dev->irqlock, flags); -		dst_mb = list_entry(ctx->dst_queue.next, -				struct s5p_mfc_buf, list); -		list_del(&dst_mb->list); -		ctx->dst_queue_cnt--; -		vb2_set_plane_payload(dst_mb->b, 0, -			s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev)); -		vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE); +		if (!list_empty(&ctx->dst_queue)) { +			dst_mb = list_entry(ctx->dst_queue.next, +					struct s5p_mfc_buf, list); +			list_del(&dst_mb->list); +			ctx->dst_queue_cnt--; +			vb2_set_plane_payload(dst_mb->b, 0, +				s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, +						dev)); +			vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE); +		}  		spin_unlock_irqrestore(&dev->irqlock, flags);  	} @@ -825,8 +900,7 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)  		mfc_debug(2, "enc src count: %d, enc ref count: %d\n",  			  ctx->src_queue_cnt, ctx->ref_queue_cnt);  	} -	if (strm_size > 0) { -		/* at least one more dest. buffers exist always  */ +	if ((ctx->dst_queue_cnt > 0) && (strm_size > 0)) {  		mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,  									list);  		list_del(&mb_entry->list); @@ -879,8 +953,10 @@ static int vidioc_querycap(struct file *file, void *priv,  	return 0;  } -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out) +static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, +							bool mplane, bool out)  { +	struct s5p_mfc_dev *dev = video_drvdata(file);  	struct s5p_mfc_fmt *fmt;  	int i, j = 0; @@ -893,6 +969,9 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)  			continue;  		else if (!out && formats[i].type != MFC_FMT_ENC)  			continue; +		else if ((dev->variant->version_bit & formats[i].versions) == 0) +			continue; +  		if (j == f->index) {  			fmt = &formats[i];  			strlcpy(f->description, fmt->name, @@ -908,25 +987,25 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)  static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,  				   struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, false, false); +	return vidioc_enum_fmt(file, f, false, false);  }  static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,  					  struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, true, false); +	return vidioc_enum_fmt(file, f, true, false);  }  static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,  				   struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, false, true); +	return vidioc_enum_fmt(file, f, false, true);  }  static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,  					  struct v4l2_fmtdesc *f)  { -	return vidioc_enum_fmt(f, true, true); +	return vidioc_enum_fmt(file, f, true, true);  }  static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) @@ -977,16 +1056,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)  			mfc_err("failed to try output format\n");  			return -EINVAL;  		} - -		if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) { -			mfc_err("VP8 is supported only in MFC v7\n"); -			return -EINVAL; -		} -  		if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {  			mfc_err("must be set encoding output size\n");  			return -EINVAL;  		} +		if ((dev->variant->version_bit & fmt->versions) == 0) { +			mfc_err("Unsupported format by this MFC version.\n"); +			return -EINVAL; +		}  		pix_fmt_mp->plane_fmt[0].bytesperline =  			pix_fmt_mp->plane_fmt[0].sizeimage; @@ -997,22 +1074,15 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)  			return -EINVAL;  		} -		if (!IS_MFCV6_PLUS(dev)) { -			if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) { -				mfc_err("Not supported format.\n"); -				return -EINVAL; -			} -		} else if (IS_MFCV6_PLUS(dev)) { -			if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) { -				mfc_err("Not supported format.\n"); -				return -EINVAL; -			} -		} -  		if (fmt->num_planes != pix_fmt_mp->num_planes) {  			mfc_err("failed to try output format\n");  			return -EINVAL;  		} +		if ((dev->variant->version_bit & fmt->versions) == 0) { +			mfc_err("Unsupported format by this MFC version.\n"); +			return -EINVAL; +		} +  		v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1,  			&pix_fmt_mp->height, 4, 1080, 1, 0);  	} else { @@ -1046,20 +1116,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)  		pix_fmt_mp->plane_fmt[0].bytesperline = 0;  		ctx->dst_bufs_cnt = 0;  		ctx->capture_state = QUEUE_FREE; -		s5p_mfc_hw_call(dev->mfc_ops, alloc_instance_buffer, ctx); -		set_work_bit_irqsave(ctx); -		s5p_mfc_clean_ctx_int_flags(ctx); -		s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); -		if (s5p_mfc_wait_for_done_ctx(ctx, \ -				S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 1)) { -				/* Error or timeout */ -			mfc_err("Error getting instance from hardware\n"); -			s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, -					ctx); -			ret = -EIO; -			goto out; -		} -		mfc_debug(2, "Got instance number: %d\n", ctx->inst_no); +		ret = s5p_mfc_open_mfc_inst(dev, ctx);  	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {  		/* src_fmt is validated by call to vidioc_try_fmt */  		ctx->src_fmt = find_format(f, MFC_FMT_RAW); @@ -1080,7 +1137,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)  		ctx->output_state = QUEUE_FREE;  	} else {  		mfc_err("invalid buf type\n"); -		return -EINVAL; +		ret = -EINVAL;  	}  out:  	mfc_debug_leave(); @@ -1377,6 +1434,12 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)  	case V4L2_CID_MPEG_VIDEO_VBV_SIZE:  		p->vbv_size = ctrl->val;  		break; +	case V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE: +		p->mv_h_range = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE: +		p->mv_v_range = ctrl->val; +		break;  	case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:  		p->codec.h264.cpb_size = ctrl->val;  		break; @@ -1557,6 +1620,21 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)  	case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:  		p->codec.vp8.golden_frame_sel = ctrl->val;  		break; +	case V4L2_CID_MPEG_VIDEO_VPX_MIN_QP: +		p->codec.vp8.rc_min_qp = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_VPX_MAX_QP: +		p->codec.vp8.rc_max_qp = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_VPX_I_FRAME_QP: +		p->codec.vp8.rc_frame_qp = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: +		p->codec.vp8.rc_p_frame_qp = ctrl->val; +		break; +	case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: +		p->codec.vp8.profile = ctrl->val; +		break;  	default:  		v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",  							ctrl->id, ctrl->val); @@ -1863,7 +1941,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)  		if (ctx->src_bufs_cnt < ctx->pb_count) {  			mfc_err("Need minimum %d OUTPUT buffers\n",  					ctx->pb_count); -			return -EINVAL; +			return -ENOBUFS;  		}  	} @@ -1875,7 +1953,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)  	return 0;  } -static int s5p_mfc_stop_streaming(struct vb2_queue *q) +static void s5p_mfc_stop_streaming(struct vb2_queue *q)  {  	unsigned long flags;  	struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); @@ -1904,7 +1982,6 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)  		ctx->src_queue_cnt = 0;  	}  	spin_unlock_irqrestore(&dev->irqlock, flags); -	return 0;  }  static void s5p_mfc_buf_queue(struct vb2_buffer *vb) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 3c01c339d69..c9a227428e6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -31,6 +31,12 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)  	dev->mfc_ops = s5p_mfc_ops;  } +void s5p_mfc_init_regs(struct s5p_mfc_dev *dev) +{ +	if (IS_MFCV6_PLUS(dev)) +		dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev); +} +  int s5p_mfc_alloc_priv_buf(struct device *dev,  					struct s5p_mfc_priv_buf *b)  { diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 754c540e7a7..7a7ad32ee60 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -17,6 +17,259 @@  #include "s5p_mfc_common.h" +struct s5p_mfc_regs { + +	/* codec common registers */ +	void *risc_on; +	void *risc2host_int; +	void *host2risc_int; +	void *risc_base_address; +	void *mfc_reset; +	void *host2risc_command; +	void *risc2host_command; +	void *mfc_bus_reset_ctrl; +	void *firmware_version; +	void *instance_id; +	void *codec_type; +	void *context_mem_addr; +	void *context_mem_size; +	void *pixel_format; +	void *metadata_enable; +	void *mfc_version; +	void *dbg_info_enable; +	void *dbg_buffer_addr; +	void *dbg_buffer_size; +	void *hed_control; +	void *mfc_timeout_value; +	void *hed_shared_mem_addr; +	void *dis_shared_mem_addr;/* only v7 */ +	void *ret_instance_id; +	void *error_code; +	void *dbg_buffer_output_size; +	void *metadata_status; +	void *metadata_addr_mb_info; +	void *metadata_size_mb_info; +	void *dbg_info_stage_counter; + +	/* decoder registers */ +	void *d_crc_ctrl; +	void *d_dec_options; +	void *d_display_delay; +	void *d_set_frame_width; +	void *d_set_frame_height; +	void *d_sei_enable; +	void *d_min_num_dpb; +	void *d_min_first_plane_dpb_size; +	void *d_min_second_plane_dpb_size; +	void *d_min_third_plane_dpb_size;/* only v8 */ +	void *d_min_num_mv; +	void *d_mvc_num_views; +	void *d_min_num_dis;/* only v7 */ +	void *d_min_first_dis_size;/* only v7 */ +	void *d_min_second_dis_size;/* only v7 */ +	void *d_min_third_dis_size;/* only v7 */ +	void *d_post_filter_luma_dpb0;/*  v7 and v8 */ +	void *d_post_filter_luma_dpb1;/* v7 and v8 */ +	void *d_post_filter_luma_dpb2;/* only v7 */ +	void *d_post_filter_chroma_dpb0;/* v7 and v8 */ +	void *d_post_filter_chroma_dpb1;/* v7 and v8 */ +	void *d_post_filter_chroma_dpb2;/* only v7 */ +	void *d_num_dpb; +	void *d_num_mv; +	void *d_init_buffer_options; +	void *d_first_plane_dpb_stride_size;/* only v8 */ +	void *d_second_plane_dpb_stride_size;/* only v8 */ +	void *d_third_plane_dpb_stride_size;/* only v8 */ +	void *d_first_plane_dpb_size; +	void *d_second_plane_dpb_size; +	void *d_third_plane_dpb_size;/* only v8 */ +	void *d_mv_buffer_size; +	void *d_first_plane_dpb; +	void *d_second_plane_dpb; +	void *d_third_plane_dpb; +	void *d_mv_buffer; +	void *d_scratch_buffer_addr; +	void *d_scratch_buffer_size; +	void *d_metadata_buffer_addr; +	void *d_metadata_buffer_size; +	void *d_nal_start_options;/* v7 and v8 */ +	void *d_cpb_buffer_addr; +	void *d_cpb_buffer_size; +	void *d_available_dpb_flag_upper; +	void *d_available_dpb_flag_lower; +	void *d_cpb_buffer_offset; +	void *d_slice_if_enable; +	void *d_picture_tag; +	void *d_stream_data_size; +	void *d_dynamic_dpb_flag_upper;/* v7 and v8 */ +	void *d_dynamic_dpb_flag_lower;/* v7 and v8 */ +	void *d_display_frame_width; +	void *d_display_frame_height; +	void *d_display_status; +	void *d_display_first_plane_addr; +	void *d_display_second_plane_addr; +	void *d_display_third_plane_addr;/* only v8 */ +	void *d_display_frame_type; +	void *d_display_crop_info1; +	void *d_display_crop_info2; +	void *d_display_picture_profile; +	void *d_display_luma_crc;/* v7 and v8 */ +	void *d_display_chroma0_crc;/* v7 and v8 */ +	void *d_display_chroma1_crc;/* only v8 */ +	void *d_display_luma_crc_top;/* only v6 */ +	void *d_display_chroma_crc_top;/* only v6 */ +	void *d_display_luma_crc_bot;/* only v6 */ +	void *d_display_chroma_crc_bot;/* only v6 */ +	void *d_display_aspect_ratio; +	void *d_display_extended_ar; +	void *d_decoded_frame_width; +	void *d_decoded_frame_height; +	void *d_decoded_status; +	void *d_decoded_first_plane_addr; +	void *d_decoded_second_plane_addr; +	void *d_decoded_third_plane_addr;/* only v8 */ +	void *d_decoded_frame_type; +	void *d_decoded_crop_info1; +	void *d_decoded_crop_info2; +	void *d_decoded_picture_profile; +	void *d_decoded_nal_size; +	void *d_decoded_luma_crc; +	void *d_decoded_chroma0_crc; +	void *d_decoded_chroma1_crc;/* only v8 */ +	void *d_ret_picture_tag_top; +	void *d_ret_picture_tag_bot; +	void *d_ret_picture_time_top; +	void *d_ret_picture_time_bot; +	void *d_chroma_format; +	void *d_vc1_info;/* v7 and v8 */ +	void *d_mpeg4_info; +	void *d_h264_info; +	void *d_metadata_addr_concealed_mb; +	void *d_metadata_size_concealed_mb; +	void *d_metadata_addr_vc1_param; +	void *d_metadata_size_vc1_param; +	void *d_metadata_addr_sei_nal; +	void *d_metadata_size_sei_nal; +	void *d_metadata_addr_vui; +	void *d_metadata_size_vui; +	void *d_metadata_addr_mvcvui;/* v7 and v8 */ +	void *d_metadata_size_mvcvui;/* v7 and v8 */ +	void *d_mvc_view_id; +	void *d_frame_pack_sei_avail; +	void *d_frame_pack_arrgment_id; +	void *d_frame_pack_sei_info; +	void *d_frame_pack_grid_pos; +	void *d_display_recovery_sei_info;/* v7 and v8 */ +	void *d_decoded_recovery_sei_info;/* v7 and v8 */ +	void *d_display_first_addr;/* only v7 */ +	void *d_display_second_addr;/* only v7 */ +	void *d_display_third_addr;/* only v7 */ +	void *d_decoded_first_addr;/* only v7 */ +	void *d_decoded_second_addr;/* only v7 */ +	void *d_decoded_third_addr;/* only v7 */ +	void *d_used_dpb_flag_upper;/* v7 and v8 */ +	void *d_used_dpb_flag_lower;/* v7 and v8 */ + +	/* encoder registers */ +	void *e_frame_width; +	void *e_frame_height; +	void *e_cropped_frame_width; +	void *e_cropped_frame_height; +	void *e_frame_crop_offset; +	void *e_enc_options; +	void *e_picture_profile; +	void *e_vbv_buffer_size; +	void *e_vbv_init_delay; +	void *e_fixed_picture_qp; +	void *e_rc_config; +	void *e_rc_qp_bound; +	void *e_rc_qp_bound_pb;/* v7 and v8 */ +	void *e_rc_mode; +	void *e_mb_rc_config; +	void *e_padding_ctrl; +	void *e_air_threshold; +	void *e_mv_hor_range; +	void *e_mv_ver_range; +	void *e_num_dpb; +	void *e_luma_dpb; +	void *e_chroma_dpb; +	void *e_me_buffer; +	void *e_scratch_buffer_addr; +	void *e_scratch_buffer_size; +	void *e_tmv_buffer0; +	void *e_tmv_buffer1; +	void *e_ir_buffer_addr;/* v7 and v8 */ +	void *e_source_first_plane_addr; +	void *e_source_second_plane_addr; +	void *e_source_third_plane_addr;/* v7 and v8 */ +	void *e_source_first_plane_stride;/* v7 and v8 */ +	void *e_source_second_plane_stride;/* v7 and v8 */ +	void *e_source_third_plane_stride;/* v7 and v8 */ +	void *e_stream_buffer_addr; +	void *e_stream_buffer_size; +	void *e_roi_buffer_addr; +	void *e_param_change; +	void *e_ir_size; +	void *e_gop_config; +	void *e_mslice_mode; +	void *e_mslice_size_mb; +	void *e_mslice_size_bits; +	void *e_frame_insertion; +	void *e_rc_frame_rate; +	void *e_rc_bit_rate; +	void *e_rc_roi_ctrl; +	void *e_picture_tag; +	void *e_bit_count_enable; +	void *e_max_bit_count; +	void *e_min_bit_count; +	void *e_metadata_buffer_addr; +	void *e_metadata_buffer_size; +	void *e_encoded_source_first_plane_addr; +	void *e_encoded_source_second_plane_addr; +	void *e_encoded_source_third_plane_addr;/* v7 and v8 */ +	void *e_stream_size; +	void *e_slice_type; +	void *e_picture_count; +	void *e_ret_picture_tag; +	void *e_stream_buffer_write_pointer; /*  only v6 */ +	void *e_recon_luma_dpb_addr; +	void *e_recon_chroma_dpb_addr; +	void *e_metadata_addr_enc_slice; +	void *e_metadata_size_enc_slice; +	void *e_mpeg4_options; +	void *e_mpeg4_hec_period; +	void *e_aspect_ratio; +	void *e_extended_sar; +	void *e_h264_options; +	void *e_h264_options_2;/* v7 and v8 */ +	void *e_h264_lf_alpha_offset; +	void *e_h264_lf_beta_offset; +	void *e_h264_i_period; +	void *e_h264_fmo_slice_grp_map_type; +	void *e_h264_fmo_num_slice_grp_minus1; +	void *e_h264_fmo_slice_grp_change_dir; +	void *e_h264_fmo_slice_grp_change_rate_minus1; +	void *e_h264_fmo_run_length_minus1_0; +	void *e_h264_aso_slice_order_0; +	void *e_h264_chroma_qp_offset; +	void *e_h264_num_t_layer; +	void *e_h264_hierarchical_qp_layer0; +	void *e_h264_frame_packing_sei_info; +	void *e_h264_nal_control;/* v7 and v8 */ +	void *e_mvc_frame_qp_view1; +	void *e_mvc_rc_bit_rate_view1; +	void *e_mvc_rc_qbound_view1; +	void *e_mvc_rc_mode_view1; +	void *e_mvc_inter_view_prediction_on; +	void *e_vp8_options;/* v7 and v8 */ +	void *e_vp8_filter_options;/* v7 and v8 */ +	void *e_vp8_golden_frame_option;/* v7 and v8 */ +	void *e_vp8_num_t_layer;/* v7 and v8 */ +	void *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */ +	void *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */ +	void *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */ +}; +  struct s5p_mfc_hw_ops {  	int (*alloc_dec_temp_buffers)(struct s5p_mfc_ctx *ctx);  	void (*release_dec_desc_buffer)(struct s5p_mfc_ctx *ctx); @@ -80,6 +333,7 @@ struct s5p_mfc_hw_ops {  };  void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); +void s5p_mfc_init_regs(struct s5p_mfc_dev *dev);  int s5p_mfc_alloc_priv_buf(struct device *dev,  					struct s5p_mfc_priv_buf *b);  void s5p_mfc_release_priv_buf(struct device *dev, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 368582b091b..58ec7bb26eb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1582,7 +1582,7 @@ static int s5p_mfc_get_int_reason_v5(struct s5p_mfc_dev *dev)  		break;  	default:  		reason = S5P_MFC_R2H_CMD_EMPTY; -	}; +	}  	return reason;  } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 461358c4a79..4f5e0ead90c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -43,10 +43,10 @@  	} while (0)  #endif /* S5P_MFC_DEBUG_REGWRITE */ -#define READL(offset)		readl(dev->regs_base + (offset)) -#define WRITEL(data, offset)	writel((data), dev->regs_base + (offset)) -#define OFFSETA(x)		(((x) - dev->port_a) >> S5P_FIMV_MEM_OFFSET) -#define OFFSETB(x)		(((x) - dev->port_b) >> S5P_FIMV_MEM_OFFSET) +#define READL(reg) \ +	(WARN_ON_ONCE(!(reg)) ? 0 : readl(reg)) +#define WRITEL(data, reg) \ +	(WARN_ON_ONCE(!(reg)) ? 0 : writel((data), (reg)))  /* Allocate temporary buffers for decoding */  static int s5p_mfc_alloc_dec_temp_buffers_v6(struct s5p_mfc_ctx *ctx) @@ -77,7 +77,12 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  			  ctx->luma_size, ctx->chroma_size, ctx->mv_size);  		mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);  	} else if (ctx->type == MFCINST_ENCODER) { -		ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * +		if (IS_MFCV8(dev)) +			ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 * +			ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V8(mb_width, mb_height), +			S5P_FIMV_TMV_BUFFER_ALIGN_V6); +		else +			ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *  			ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),  			S5P_FIMV_TMV_BUFFER_ALIGN_V6); @@ -87,10 +92,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  		ctx->chroma_dpb_size = ALIGN((mb_width * mb_height) *  				S5P_FIMV_CHROMA_MB_TO_PIXEL_V6,  				S5P_FIMV_CHROMA_DPB_BUFFER_ALIGN_V6); -		ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6( -					ctx->img_width, ctx->img_height, -					mb_width, mb_height), -					S5P_FIMV_ME_BUFFER_ALIGN_V6); +		if (IS_MFCV8(dev)) +			ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V8( +						ctx->img_width, ctx->img_height, +						mb_width, mb_height), +						S5P_FIMV_ME_BUFFER_ALIGN_V6); +		else +			ctx->me_buffer_size = ALIGN(S5P_FIMV_ME_BUFFER_SIZE_V6( +						ctx->img_width, ctx->img_height, +						mb_width, mb_height), +						S5P_FIMV_ME_BUFFER_ALIGN_V6);  		mfc_debug(2, "recon luma size: %d chroma size: %d\n",  			  ctx->luma_dpb_size, ctx->chroma_dpb_size); @@ -102,8 +113,14 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  	switch (ctx->codec_mode) {  	case S5P_MFC_CODEC_H264_DEC:  	case S5P_MFC_CODEC_H264_MVC_DEC: -		ctx->scratch_buf_size = -			S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6( +		if (IS_MFCV8(dev)) +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V8( +					mb_width, +					mb_height); +		else +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(  					mb_width,  					mb_height);  		ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, @@ -113,7 +130,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  			(ctx->mv_count * ctx->mv_size);  		break;  	case S5P_MFC_CODEC_MPEG4_DEC: -		if (IS_MFCV7(dev)) { +		if (IS_MFCV7_PLUS(dev)) {  			ctx->scratch_buf_size =  				S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(  						mb_width, @@ -153,19 +170,31 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  		ctx->bank1.size = ctx->scratch_buf_size;  		break;  	case S5P_MFC_CODEC_VP8_DEC: -		ctx->scratch_buf_size = -			S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6( -					mb_width, -					mb_height); +		if (IS_MFCV8(dev)) +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V8( +						mb_width, +						mb_height); +		else +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_VP8_DEC_V6( +						mb_width, +						mb_height);  		ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,  				S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);  		ctx->bank1.size = ctx->scratch_buf_size;  		break;  	case S5P_MFC_CODEC_H264_ENC: -		ctx->scratch_buf_size = -			S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6( +		if (IS_MFCV8(dev)) +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V8(  					mb_width,  					mb_height); +		else +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_H264_ENC_V6( +						mb_width, +						mb_height);  		ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,  				S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);  		ctx->bank1.size = @@ -189,10 +218,16 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)  		ctx->bank2.size = 0;  		break;  	case S5P_MFC_CODEC_VP8_ENC: -		ctx->scratch_buf_size = -			S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7( +		if (IS_MFCV8(dev)) +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V8(  					mb_width,  					mb_height); +		else +			ctx->scratch_buf_size = +				S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7( +						mb_width, +						mb_height);  		ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,  				S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);  		ctx->bank1.size = @@ -332,6 +367,12 @@ static void s5p_mfc_dec_calc_dpb_size_v6(struct s5p_mfc_ctx *ctx)  	ctx->luma_size = calc_plane(ctx->img_width, ctx->img_height);  	ctx->chroma_size = calc_plane(ctx->img_width, (ctx->img_height >> 1)); +	if (IS_MFCV8(ctx->dev)) { +		/* MFCv8 needs additional 64 bytes for luma,chroma dpb*/ +		ctx->luma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; +		ctx->chroma_size += S5P_FIMV_D_ALIGN_PLANE_SIZE_V8; +	} +  	if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||  			ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) {  		ctx->mv_size = S5P_MFC_DEC_MV_SIZE_V6(ctx->img_width, @@ -354,7 +395,7 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)  	ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256);  	/* MFCv7 needs pad bytes for Luma and Chroma */ -	if (IS_MFCV7(ctx->dev)) { +	if (IS_MFCV7_PLUS(ctx->dev)) {  		ctx->luma_size += MFC_LUMA_PAD_BYTES_V7;  		ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7;  	} @@ -366,16 +407,17 @@ static int s5p_mfc_set_dec_stream_buffer_v6(struct s5p_mfc_ctx *ctx,  			unsigned int strm_size)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_buf_size *buf_size = dev->variant->buf_size;  	mfc_debug_enter();  	mfc_debug(2, "inst_no: %d, buf_addr: 0x%08x,\n"  		"buf_size: 0x%08x (%d)\n",  		ctx->inst_no, buf_addr, strm_size, strm_size); -	WRITEL(strm_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6); -	WRITEL(buf_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6); -	WRITEL(buf_size->cpb, S5P_FIMV_D_CPB_BUFFER_SIZE_V6); -	WRITEL(start_num_byte, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6); +	WRITEL(strm_size, mfc_regs->d_stream_data_size); +	WRITEL(buf_addr, mfc_regs->d_cpb_buffer_addr); +	WRITEL(buf_size->cpb, mfc_regs->d_cpb_buffer_size); +	WRITEL(start_num_byte, mfc_regs->d_cpb_buffer_offset);  	mfc_debug_leave();  	return 0; @@ -387,6 +429,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)  	unsigned int frame_size, i;  	unsigned int frame_size_ch, frame_size_mv;  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	size_t buf_addr1;  	int buf_size1;  	int align_gap; @@ -398,19 +441,27 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)  	mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count);  	mfc_debug(2, "Setting display delay to %d\n", ctx->display_delay); -	WRITEL(ctx->total_dpb_count, S5P_FIMV_D_NUM_DPB_V6); -	WRITEL(ctx->luma_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6); -	WRITEL(ctx->chroma_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6); +	WRITEL(ctx->total_dpb_count, mfc_regs->d_num_dpb); +	WRITEL(ctx->luma_size, mfc_regs->d_first_plane_dpb_size); +	WRITEL(ctx->chroma_size, mfc_regs->d_second_plane_dpb_size); + +	WRITEL(buf_addr1, mfc_regs->d_scratch_buffer_addr); +	WRITEL(ctx->scratch_buf_size, mfc_regs->d_scratch_buffer_size); + +	if (IS_MFCV8(dev)) { +		WRITEL(ctx->img_width, +			mfc_regs->d_first_plane_dpb_stride_size); +		WRITEL(ctx->img_width, +			mfc_regs->d_second_plane_dpb_stride_size); +	} -	WRITEL(buf_addr1, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6); -	WRITEL(ctx->scratch_buf_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6);  	buf_addr1 += ctx->scratch_buf_size;  	buf_size1 -= ctx->scratch_buf_size;  	if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||  			ctx->codec_mode == S5P_FIMV_CODEC_H264_MVC_DEC){ -		WRITEL(ctx->mv_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6); -		WRITEL(ctx->mv_count, S5P_FIMV_D_NUM_MV_V6); +		WRITEL(ctx->mv_size, mfc_regs->d_mv_buffer_size); +		WRITEL(ctx->mv_count, mfc_regs->d_num_mv);  	}  	frame_size = ctx->luma_size; @@ -424,11 +475,11 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)  		mfc_debug(2, "Luma %d: %x\n", i,  					ctx->dst_bufs[i].cookie.raw.luma);  		WRITEL(ctx->dst_bufs[i].cookie.raw.luma, -				S5P_FIMV_D_LUMA_DPB_V6 + i * 4); +				mfc_regs->d_first_plane_dpb + i * 4);  		mfc_debug(2, "\tChroma %d: %x\n", i,  					ctx->dst_bufs[i].cookie.raw.chroma);  		WRITEL(ctx->dst_bufs[i].cookie.raw.chroma, -				S5P_FIMV_D_CHROMA_DPB_V6 + i * 4); +				mfc_regs->d_second_plane_dpb + i * 4);  	}  	if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||  			ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) { @@ -441,7 +492,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)  			mfc_debug(2, "\tBuf1: %x, size: %d\n",  					buf_addr1, buf_size1); -			WRITEL(buf_addr1, S5P_FIMV_D_MV_BUFFER_V6 + i * 4); +			WRITEL(buf_addr1, mfc_regs->d_mv_buffer + i * 4);  			buf_addr1 += frame_size_mv;  			buf_size1 -= frame_size_mv;  		} @@ -454,7 +505,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)  		return -ENOMEM;  	} -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  			S5P_FIMV_CH_INIT_BUFS_V6, NULL); @@ -467,9 +518,10 @@ static int s5p_mfc_set_enc_stream_buffer_v6(struct s5p_mfc_ctx *ctx,  		unsigned long addr, unsigned int size)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; -	WRITEL(addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); /* 16B align */ -	WRITEL(size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6); +	WRITEL(addr, mfc_regs->e_stream_buffer_addr); /* 16B align */ +	WRITEL(size, mfc_regs->e_stream_buffer_size);  	mfc_debug(2, "stream buf addr: 0x%08lx, size: 0x%d\n",  		  addr, size); @@ -481,14 +533,10 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,  		unsigned long y_addr, unsigned long c_addr)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; -	if (IS_MFCV7(dev)) { -		WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7); -		WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7); -	} else { -		WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); -		WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); -	} +	WRITEL(y_addr, mfc_regs->e_source_first_plane_addr); +	WRITEL(c_addr, mfc_regs->e_source_second_plane_addr);  	mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);  	mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr); @@ -498,18 +546,14 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,  		unsigned long *y_addr, unsigned long *c_addr)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	unsigned long enc_recon_y_addr, enc_recon_c_addr; -	if (IS_MFCV7(dev)) { -		*y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); -		*c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); -	} else { -		*y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); -		*c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); -	} +	*y_addr = READL(mfc_regs->e_encoded_source_first_plane_addr); +	*c_addr = READL(mfc_regs->e_encoded_source_second_plane_addr); -	enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6); -	enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6); +	enc_recon_y_addr = READL(mfc_regs->e_recon_luma_dpb_addr); +	enc_recon_c_addr = READL(mfc_regs->e_recon_chroma_dpb_addr);  	mfc_debug(2, "recon y addr: 0x%08lx\n", enc_recon_y_addr);  	mfc_debug(2, "recon c addr: 0x%08lx\n", enc_recon_c_addr); @@ -519,6 +563,7 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,  static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	size_t buf_addr1;  	int i, buf_size1; @@ -530,24 +575,24 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)  	mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1);  	for (i = 0; i < ctx->pb_count; i++) { -		WRITEL(buf_addr1, S5P_FIMV_E_LUMA_DPB_V6 + (4 * i)); +		WRITEL(buf_addr1, mfc_regs->e_luma_dpb + (4 * i));  		buf_addr1 += ctx->luma_dpb_size; -		WRITEL(buf_addr1, S5P_FIMV_E_CHROMA_DPB_V6 + (4 * i)); +		WRITEL(buf_addr1, mfc_regs->e_chroma_dpb + (4 * i));  		buf_addr1 += ctx->chroma_dpb_size; -		WRITEL(buf_addr1, S5P_FIMV_E_ME_BUFFER_V6 + (4 * i)); +		WRITEL(buf_addr1, mfc_regs->e_me_buffer + (4 * i));  		buf_addr1 += ctx->me_buffer_size;  		buf_size1 -= (ctx->luma_dpb_size + ctx->chroma_dpb_size +  			ctx->me_buffer_size);  	} -	WRITEL(buf_addr1, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6); -	WRITEL(ctx->scratch_buf_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6); +	WRITEL(buf_addr1, mfc_regs->e_scratch_buffer_addr); +	WRITEL(ctx->scratch_buf_size, mfc_regs->e_scratch_buffer_size);  	buf_addr1 += ctx->scratch_buf_size;  	buf_size1 -= ctx->scratch_buf_size; -	WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER0_V6); +	WRITEL(buf_addr1, mfc_regs->e_tmv_buffer0);  	buf_addr1 += ctx->tmv_buffer_size >> 1; -	WRITEL(buf_addr1, S5P_FIMV_E_TMV_BUFFER1_V6); +	WRITEL(buf_addr1, mfc_regs->e_tmv_buffer1);  	buf_addr1 += ctx->tmv_buffer_size >> 1;  	buf_size1 -= ctx->tmv_buffer_size; @@ -558,7 +603,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)  		return -ENOMEM;  	} -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  			S5P_FIMV_CH_INIT_BUFS_V6, NULL); @@ -570,18 +615,19 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	/* multi-slice control */  	/* multi-slice MB number or bit size */ -	WRITEL(ctx->slice_mode, S5P_FIMV_E_MSLICE_MODE_V6); +	WRITEL(ctx->slice_mode, mfc_regs->e_mslice_mode);  	if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { -		WRITEL(ctx->slice_size.mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6); +		WRITEL(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb);  	} else if (ctx->slice_mode ==  			V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { -		WRITEL(ctx->slice_size.bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); +		WRITEL(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits);  	} else { -		WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_MB_V6); -		WRITEL(0x0, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); +		WRITEL(0x0, mfc_regs->e_mslice_size_mb); +		WRITEL(0x0, mfc_regs->e_mslice_size_bits);  	}  	return 0; @@ -590,27 +636,28 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	unsigned int reg = 0;  	mfc_debug_enter();  	/* width */ -	WRITEL(ctx->img_width, S5P_FIMV_E_FRAME_WIDTH_V6); /* 16 align */ +	WRITEL(ctx->img_width, mfc_regs->e_frame_width); /* 16 align */  	/* height */ -	WRITEL(ctx->img_height, S5P_FIMV_E_FRAME_HEIGHT_V6); /* 16 align */ +	WRITEL(ctx->img_height, mfc_regs->e_frame_height); /* 16 align */  	/* cropped width */ -	WRITEL(ctx->img_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6); +	WRITEL(ctx->img_width, mfc_regs->e_cropped_frame_width);  	/* cropped height */ -	WRITEL(ctx->img_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); +	WRITEL(ctx->img_height, mfc_regs->e_cropped_frame_height);  	/* cropped offset */ -	WRITEL(0x0, S5P_FIMV_E_FRAME_CROP_OFFSET_V6); +	WRITEL(0x0, mfc_regs->e_frame_crop_offset);  	/* pictype : IDR period */  	reg = 0;  	reg |= p->gop_size & 0xFFFF; -	WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_gop_config);  	/* multi-slice control */  	/* multi-slice MB number or bit size */ @@ -618,65 +665,65 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)  	reg = 0;  	if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {  		reg |= (0x1 << 3); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  		ctx->slice_size.mb = p->slice_mb;  	} else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {  		reg |= (0x1 << 3); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  		ctx->slice_size.bits = p->slice_bit;  	} else {  		reg &= ~(0x1 << 3); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  	}  	s5p_mfc_set_slice_mode(ctx);  	/* cyclic intra refresh */ -	WRITEL(p->intra_refresh_mb, S5P_FIMV_E_IR_SIZE_V6); -	reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +	WRITEL(p->intra_refresh_mb, mfc_regs->e_ir_size); +	reg = READL(mfc_regs->e_enc_options);  	if (p->intra_refresh_mb == 0)  		reg &= ~(0x1 << 4);  	else  		reg |= (0x1 << 4); -	WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_enc_options);  	/* 'NON_REFERENCE_STORE_ENABLE' for debugging */ -	reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +	reg = READL(mfc_regs->e_enc_options);  	reg &= ~(0x1 << 9); -	WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_enc_options);  	/* memory structure cur. frame */  	if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {  		/* 0: Linear, 1: 2D tiled*/ -		reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +		reg = READL(mfc_regs->e_enc_options);  		reg &= ~(0x1 << 7); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  		/* 0: NV12(CbCr), 1: NV21(CrCb) */ -		WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); +		WRITEL(0x0, mfc_regs->pixel_format);  	} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV21M) {  		/* 0: Linear, 1: 2D tiled*/ -		reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +		reg = READL(mfc_regs->e_enc_options);  		reg &= ~(0x1 << 7); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  		/* 0: NV12(CbCr), 1: NV21(CrCb) */ -		WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6); +		WRITEL(0x1, mfc_regs->pixel_format);  	} else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {  		/* 0: Linear, 1: 2D tiled*/ -		reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +		reg = READL(mfc_regs->e_enc_options);  		reg |= (0x1 << 7); -		WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->e_enc_options);  		/* 0: NV12(CbCr), 1: NV21(CrCb) */ -		WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); +		WRITEL(0x0, mfc_regs->pixel_format);  	}  	/* memory structure recon. frame */  	/* 0: Linear, 1: 2D tiled */ -	reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +	reg = READL(mfc_regs->e_enc_options);  	reg |= (0x1 << 8); -	WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_enc_options);  	/* padding control & value */ -	WRITEL(0x0, S5P_FIMV_E_PADDING_CTRL_V6); +	WRITEL(0x0, mfc_regs->e_padding_ctrl);  	if (p->pad) {  		reg = 0;  		/** enable */ @@ -687,68 +734,64 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)  		reg |= ((p->pad_cb & 0xFF) << 8);  		/** y value */  		reg |= p->pad_luma & 0xFF; -		WRITEL(reg, S5P_FIMV_E_PADDING_CTRL_V6); +		WRITEL(reg, mfc_regs->e_padding_ctrl);  	}  	/* rate control config. */  	reg = 0;  	/* frame-level rate control */  	reg |= ((p->rc_frame & 0x1) << 9); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* bit rate */  	if (p->rc_frame)  		WRITEL(p->rc_bitrate, -			S5P_FIMV_E_RC_BIT_RATE_V6); +			mfc_regs->e_rc_bit_rate);  	else -		WRITEL(1, S5P_FIMV_E_RC_BIT_RATE_V6); +		WRITEL(1, mfc_regs->e_rc_bit_rate);  	/* reaction coefficient */  	if (p->rc_frame) {  		if (p->rc_reaction_coeff < TIGHT_CBR_MAX) /* tight CBR */ -			WRITEL(1, S5P_FIMV_E_RC_RPARAM_V6); +			WRITEL(1, mfc_regs->e_rc_mode);  		else					  /* loose CBR */ -			WRITEL(2, S5P_FIMV_E_RC_RPARAM_V6); +			WRITEL(2, mfc_regs->e_rc_mode);  	}  	/* seq header ctrl */ -	reg = READL(S5P_FIMV_E_ENC_OPTIONS_V6); +	reg = READL(mfc_regs->e_enc_options);  	reg &= ~(0x1 << 2);  	reg |= ((p->seq_hdr_mode & 0x1) << 2);  	/* frame skip mode */  	reg &= ~(0x3);  	reg |= (p->frame_skip_mode & 0x3); -	WRITEL(reg, S5P_FIMV_E_ENC_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_enc_options);  	/* 'DROP_CONTROL_ENABLE', disable */ -	reg = READL(S5P_FIMV_E_RC_CONFIG_V6); +	reg = READL(mfc_regs->e_rc_config);  	reg &= ~(0x1 << 10); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* setting for MV range [16, 256] */ -	reg = 0; -	reg &= ~(0x3FFF); -	reg = 256; -	WRITEL(reg, S5P_FIMV_E_MV_HOR_RANGE_V6); +	reg = (p->mv_h_range & S5P_FIMV_E_MV_RANGE_V6_MASK); +	WRITEL(reg, mfc_regs->e_mv_hor_range); -	reg = 0; -	reg &= ~(0x3FFF); -	reg = 256; -	WRITEL(reg, S5P_FIMV_E_MV_VER_RANGE_V6); +	reg = (p->mv_v_range & S5P_FIMV_E_MV_RANGE_V6_MASK); +	WRITEL(reg, mfc_regs->e_mv_ver_range); -	WRITEL(0x0, S5P_FIMV_E_FRAME_INSERTION_V6); -	WRITEL(0x0, S5P_FIMV_E_ROI_BUFFER_ADDR_V6); -	WRITEL(0x0, S5P_FIMV_E_PARAM_CHANGE_V6); -	WRITEL(0x0, S5P_FIMV_E_RC_ROI_CTRL_V6); -	WRITEL(0x0, S5P_FIMV_E_PICTURE_TAG_V6); +	WRITEL(0x0, mfc_regs->e_frame_insertion); +	WRITEL(0x0, mfc_regs->e_roi_buffer_addr); +	WRITEL(0x0, mfc_regs->e_param_change); +	WRITEL(0x0, mfc_regs->e_rc_roi_ctrl); +	WRITEL(0x0, mfc_regs->e_picture_tag); -	WRITEL(0x0, S5P_FIMV_E_BIT_COUNT_ENABLE_V6); -	WRITEL(0x0, S5P_FIMV_E_MAX_BIT_COUNT_V6); -	WRITEL(0x0, S5P_FIMV_E_MIN_BIT_COUNT_V6); +	WRITEL(0x0, mfc_regs->e_bit_count_enable); +	WRITEL(0x0, mfc_regs->e_max_bit_count); +	WRITEL(0x0, mfc_regs->e_min_bit_count); -	WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6); -	WRITEL(0x0, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6); +	WRITEL(0x0, mfc_regs->e_metadata_buffer_addr); +	WRITEL(0x0, mfc_regs->e_metadata_buffer_size);  	mfc_debug_leave(); @@ -758,6 +801,7 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;  	unsigned int reg = 0; @@ -768,10 +812,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  	s5p_mfc_set_enc_params(ctx);  	/* pictype : number of B */ -	reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); +	reg = READL(mfc_regs->e_gop_config);  	reg &= ~(0x3 << 16);  	reg |= ((p->num_b_frame & 0x3) << 16); -	WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_gop_config);  	/* profile & level */  	reg = 0; @@ -779,18 +823,19 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  	reg |= ((p_h264->level & 0xFF) << 8);  	/** profile - 0 ~ 3 */  	reg |= p_h264->profile & 0x3F; -	WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); +	WRITEL(reg, mfc_regs->e_picture_profile);  	/* rate control config. */ -	reg = READL(S5P_FIMV_E_RC_CONFIG_V6); +	reg = READL(mfc_regs->e_rc_config);  	/** macroblock level rate control */  	reg &= ~(0x1 << 8);  	reg |= ((p->rc_mb & 0x1) << 8); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config); +  	/** frame QP */  	reg &= ~(0x3F);  	reg |= p_h264->rc_frame_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* max & min value of QP */  	reg = 0; @@ -798,16 +843,16 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  	reg |= ((p_h264->rc_max_qp & 0x3F) << 8);  	/** min QP */  	reg |= p_h264->rc_min_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); +	WRITEL(reg, mfc_regs->e_rc_qp_bound);  	/* other QPs */ -	WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +	WRITEL(0x0, mfc_regs->e_fixed_picture_qp);  	if (!p->rc_frame && !p->rc_mb) {  		reg = 0;  		reg |= ((p_h264->rc_b_frame_qp & 0x3F) << 16);  		reg |= ((p_h264->rc_p_frame_qp & 0x3F) << 8);  		reg |= p_h264->rc_frame_qp & 0x3F; -		WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +		WRITEL(reg, mfc_regs->e_fixed_picture_qp);  	}  	/* frame rate */ @@ -815,38 +860,38 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  		reg = 0;  		reg |= ((p->rc_framerate_num & 0xFFFF) << 16);  		reg |= p->rc_framerate_denom & 0xFFFF; -		WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); +		WRITEL(reg, mfc_regs->e_rc_frame_rate);  	}  	/* vbv buffer size */  	if (p->frame_skip_mode ==  			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {  		WRITEL(p_h264->cpb_size & 0xFFFF, -				S5P_FIMV_E_VBV_BUFFER_SIZE_V6); +				mfc_regs->e_vbv_buffer_size);  		if (p->rc_frame) -			WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); +			WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);  	}  	/* interlace */  	reg = 0;  	reg |= ((p_h264->interlace & 0x1) << 3); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* height */  	if (p_h264->interlace) {  		WRITEL(ctx->img_height >> 1, -				S5P_FIMV_E_FRAME_HEIGHT_V6); /* 32 align */ +				mfc_regs->e_frame_height); /* 32 align */  		/* cropped height */  		WRITEL(ctx->img_height >> 1, -				S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); +				mfc_regs->e_cropped_frame_height);  	}  	/* loop filter ctrl */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	reg = READL(mfc_regs->e_h264_options);  	reg &= ~(0x3 << 1);  	reg |= ((p_h264->loop_filter_mode & 0x3) << 1); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* loopfilter alpha offset */  	if (p_h264->loop_filter_alpha < 0) { @@ -856,7 +901,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  		reg = 0x00;  		reg |= (p_h264->loop_filter_alpha & 0xF);  	} -	WRITEL(reg, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6); +	WRITEL(reg, mfc_regs->e_h264_lf_alpha_offset);  	/* loopfilter beta offset */  	if (p_h264->loop_filter_beta < 0) { @@ -866,28 +911,28 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  		reg = 0x00;  		reg |= (p_h264->loop_filter_beta & 0xF);  	} -	WRITEL(reg, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6); +	WRITEL(reg, mfc_regs->e_h264_lf_beta_offset);  	/* entropy coding mode */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	reg = READL(mfc_regs->e_h264_options);  	reg &= ~(0x1);  	reg |= p_h264->entropy_mode & 0x1; -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* number of ref. picture */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	reg = READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 7);  	reg |= (((p_h264->num_ref_pic_4p - 1) & 0x1) << 7); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* 8x8 transform enable */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	reg = READL(mfc_regs->e_h264_options);  	reg &= ~(0x3 << 12);  	reg |= ((p_h264->_8x8_transform & 0x3) << 12); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* macroblock adaptive scaling features */ -	WRITEL(0x0, S5P_FIMV_E_MB_RC_CONFIG_V6); +	WRITEL(0x0, mfc_regs->e_mb_rc_config);  	if (p->rc_mb) {  		reg = 0;  		/** dark region */ @@ -898,92 +943,95 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  		reg |= ((p_h264->rc_mb_static & 0x1) << 1);  		/** high activity region */  		reg |= p_h264->rc_mb_activity & 0x1; -		WRITEL(reg, S5P_FIMV_E_MB_RC_CONFIG_V6); +		WRITEL(reg, mfc_regs->e_mb_rc_config);  	}  	/* aspect ratio VUI */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 5);  	reg |= ((p_h264->vui_sar & 0x1) << 5); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options); -	WRITEL(0x0, S5P_FIMV_E_ASPECT_RATIO_V6); -	WRITEL(0x0, S5P_FIMV_E_EXTENDED_SAR_V6); +	WRITEL(0x0, mfc_regs->e_aspect_ratio); +	WRITEL(0x0, mfc_regs->e_extended_sar);  	if (p_h264->vui_sar) {  		/* aspect ration IDC */  		reg = 0;  		reg |= p_h264->vui_sar_idc & 0xFF; -		WRITEL(reg, S5P_FIMV_E_ASPECT_RATIO_V6); +		WRITEL(reg, mfc_regs->e_aspect_ratio);  		if (p_h264->vui_sar_idc == 0xFF) {  			/* extended SAR */  			reg = 0;  			reg |= (p_h264->vui_ext_sar_width & 0xFFFF) << 16;  			reg |= p_h264->vui_ext_sar_height & 0xFFFF; -			WRITEL(reg, S5P_FIMV_E_EXTENDED_SAR_V6); +			WRITEL(reg, mfc_regs->e_extended_sar);  		}  	}  	/* intra picture period for H.264 open GOP */  	/* control */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 4);  	reg |= ((p_h264->open_gop & 0x1) << 4); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options); +  	/* value */ -	WRITEL(0x0, S5P_FIMV_E_H264_I_PERIOD_V6); +	WRITEL(0x0, mfc_regs->e_h264_i_period);  	if (p_h264->open_gop) {  		reg = 0;  		reg |= p_h264->open_gop_size & 0xFFFF; -		WRITEL(reg, S5P_FIMV_E_H264_I_PERIOD_V6); +		WRITEL(reg, mfc_regs->e_h264_i_period);  	}  	/* 'WEIGHTED_BI_PREDICTION' for B is disable */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x3 << 9); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* 'CONSTRAINED_INTRA_PRED_ENABLE' is disable */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 14); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* ASO */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 6);  	reg |= ((p_h264->aso & 0x1) << 6); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	/* hier qp enable */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 8);  	reg |= ((p_h264->open_gop & 0x1) << 8); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	reg = 0;  	if (p_h264->hier_qp && p_h264->hier_qp_layer) {  		reg |= (p_h264->hier_qp_type & 0x1) << 0x3;  		reg |= p_h264->hier_qp_layer & 0x7; -		WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6); +		WRITEL(reg, mfc_regs->e_h264_num_t_layer);  		/* QP value for each layer */ -		for (i = 0; i < (p_h264->hier_qp_layer & 0x7); i++) +		for (i = 0; i < p_h264->hier_qp_layer && +				i < ARRAY_SIZE(p_h264->hier_qp_layer_qp); i++) {  			WRITEL(p_h264->hier_qp_layer_qp[i], -				S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6 + -				i * 4); +				mfc_regs->e_h264_hierarchical_qp_layer0 +				+ i * 4); +		}  	}  	/* number of coding layer should be zero when hierarchical is disable */ -	WRITEL(reg, S5P_FIMV_E_H264_NUM_T_LAYER_V6); +	WRITEL(reg, mfc_regs->e_h264_num_t_layer);  	/* frame packing SEI generation */ -	reg = READL(S5P_FIMV_E_H264_OPTIONS_V6); +	READL(mfc_regs->e_h264_options);  	reg &= ~(0x1 << 25);  	reg |= ((p_h264->sei_frame_packing & 0x1) << 25); -	WRITEL(reg, S5P_FIMV_E_H264_OPTIONS_V6); +	WRITEL(reg, mfc_regs->e_h264_options);  	if (p_h264->sei_frame_packing) {  		reg = 0;  		/** current frame0 flag */  		reg |= ((p_h264->sei_fp_curr_frame_0 & 0x1) << 2);  		/** arrangement type */  		reg |= p_h264->sei_fp_arrangement_type & 0x3; -		WRITEL(reg, S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6); +		WRITEL(reg, mfc_regs->e_h264_frame_packing_sei_info);  	}  	if (p_h264->fmo) { @@ -993,8 +1041,8 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  				p_h264->fmo_slice_grp = 4;  			for (i = 0; i < (p_h264->fmo_slice_grp & 0xF); i++)  				WRITEL(p_h264->fmo_run_len[i] - 1, -				S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6 + -				i * 4); +					mfc_regs->e_h264_fmo_run_length_minus1_0 +					+ i * 4);  			break;  		case V4L2_MPEG_VIDEO_H264_FMO_MAP_TYPE_SCATTERED_SLICES:  			if (p_h264->fmo_slice_grp > 4) @@ -1005,10 +1053,10 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  			if (p_h264->fmo_slice_grp > 2)  				p_h264->fmo_slice_grp = 2;  			WRITEL(p_h264->fmo_chg_dir & 0x1, -				S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6); +				mfc_regs->e_h264_fmo_slice_grp_change_dir);  			/* the valid range is 0 ~ number of macroblocks -1 */  			WRITEL(p_h264->fmo_chg_rate, -				S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6); +			mfc_regs->e_h264_fmo_slice_grp_change_rate_minus1);  			break;  		default:  			mfc_err("Unsupported map type for FMO: %d\n", @@ -1019,11 +1067,11 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  		}  		WRITEL(p_h264->fmo_map_type, -				S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6); +				mfc_regs->e_h264_fmo_slice_grp_map_type);  		WRITEL(p_h264->fmo_slice_grp - 1, -				S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); +				mfc_regs->e_h264_fmo_num_slice_grp_minus1);  	} else { -		WRITEL(0, S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); +		WRITEL(0, mfc_regs->e_h264_fmo_num_slice_grp_minus1);  	}  	mfc_debug_leave(); @@ -1034,6 +1082,7 @@ static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;  	unsigned int reg = 0; @@ -1043,10 +1092,10 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  	s5p_mfc_set_enc_params(ctx);  	/* pictype : number of B */ -	reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); +	reg = READL(mfc_regs->e_gop_config);  	reg &= ~(0x3 << 16);  	reg |= ((p->num_b_frame & 0x3) << 16); -	WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_gop_config);  	/* profile & level */  	reg = 0; @@ -1054,18 +1103,19 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  	reg |= ((p_mpeg4->level & 0xFF) << 8);  	/** profile - 0 ~ 1 */  	reg |= p_mpeg4->profile & 0x3F; -	WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); +	WRITEL(reg, mfc_regs->e_picture_profile);  	/* rate control config. */ -	reg = READL(S5P_FIMV_E_RC_CONFIG_V6); +	reg = READL(mfc_regs->e_rc_config);  	/** macroblock level rate control */  	reg &= ~(0x1 << 8);  	reg |= ((p->rc_mb & 0x1) << 8); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config); +  	/** frame QP */  	reg &= ~(0x3F);  	reg |= p_mpeg4->rc_frame_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* max & min value of QP */  	reg = 0; @@ -1073,16 +1123,16 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  	reg |= ((p_mpeg4->rc_max_qp & 0x3F) << 8);  	/** min QP */  	reg |= p_mpeg4->rc_min_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); +	WRITEL(reg, mfc_regs->e_rc_qp_bound);  	/* other QPs */ -	WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +	WRITEL(0x0, mfc_regs->e_fixed_picture_qp);  	if (!p->rc_frame && !p->rc_mb) {  		reg = 0;  		reg |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 16);  		reg |= ((p_mpeg4->rc_p_frame_qp & 0x3F) << 8);  		reg |= p_mpeg4->rc_frame_qp & 0x3F; -		WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +		WRITEL(reg, mfc_regs->e_fixed_picture_qp);  	}  	/* frame rate */ @@ -1090,21 +1140,21 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  		reg = 0;  		reg |= ((p->rc_framerate_num & 0xFFFF) << 16);  		reg |= p->rc_framerate_denom & 0xFFFF; -		WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); +		WRITEL(reg, mfc_regs->e_rc_frame_rate);  	}  	/* vbv buffer size */  	if (p->frame_skip_mode ==  			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { -		WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); +		WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);  		if (p->rc_frame) -			WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); +			WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);  	}  	/* Disable HEC */ -	WRITEL(0x0, S5P_FIMV_E_MPEG4_OPTIONS_V6); -	WRITEL(0x0, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6); +	WRITEL(0x0, mfc_regs->e_mpeg4_options); +	WRITEL(0x0, mfc_regs->e_mpeg4_hec_period);  	mfc_debug_leave(); @@ -1114,6 +1164,7 @@ static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;  	unsigned int reg = 0; @@ -1126,18 +1177,19 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)  	reg = 0;  	/** profile */  	reg |= (0x1 << 4); -	WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); +	WRITEL(reg, mfc_regs->e_picture_profile);  	/* rate control config. */ -	reg = READL(S5P_FIMV_E_RC_CONFIG_V6); +	reg = READL(mfc_regs->e_rc_config);  	/** macroblock level rate control */  	reg &= ~(0x1 << 8);  	reg |= ((p->rc_mb & 0x1) << 8); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config); +  	/** frame QP */  	reg &= ~(0x3F);  	reg |= p_h263->rc_frame_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* max & min value of QP */  	reg = 0; @@ -1145,16 +1197,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)  	reg |= ((p_h263->rc_max_qp & 0x3F) << 8);  	/** min QP */  	reg |= p_h263->rc_min_qp & 0x3F; -	WRITEL(reg, S5P_FIMV_E_RC_QP_BOUND_V6); +	WRITEL(reg, mfc_regs->e_rc_qp_bound);  	/* other QPs */ -	WRITEL(0x0, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +	WRITEL(0x0, mfc_regs->e_fixed_picture_qp);  	if (!p->rc_frame && !p->rc_mb) {  		reg = 0;  		reg |= ((p_h263->rc_b_frame_qp & 0x3F) << 16);  		reg |= ((p_h263->rc_p_frame_qp & 0x3F) << 8);  		reg |= p_h263->rc_frame_qp & 0x3F; -		WRITEL(reg, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +		WRITEL(reg, mfc_regs->e_fixed_picture_qp);  	}  	/* frame rate */ @@ -1162,16 +1214,16 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)  		reg = 0;  		reg |= ((p->rc_framerate_num & 0xFFFF) << 16);  		reg |= p->rc_framerate_denom & 0xFFFF; -		WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); +		WRITEL(reg, mfc_regs->e_rc_frame_rate);  	}  	/* vbv buffer size */  	if (p->frame_skip_mode ==  			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { -		WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); +		WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);  		if (p->rc_frame) -			WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); +			WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);  	}  	mfc_debug_leave(); @@ -1182,6 +1234,7 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;  	unsigned int reg = 0; @@ -1192,39 +1245,57 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)  	s5p_mfc_set_enc_params(ctx);  	/* pictype : number of B */ -	reg = READL(S5P_FIMV_E_GOP_CONFIG_V6); +	reg = READL(mfc_regs->e_gop_config);  	reg &= ~(0x3 << 16);  	reg |= ((p->num_b_frame & 0x3) << 16); -	WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_gop_config); -	/* profile & level */ -	reg = 0; -	/** profile */ -	reg |= (0x1 << 4); -	WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6); +	/* profile - 0 ~ 3 */ +	reg = p_vp8->profile & 0x3; +	WRITEL(reg, mfc_regs->e_picture_profile);  	/* rate control config. */ -	reg = READL(S5P_FIMV_E_RC_CONFIG_V6); +	reg = READL(mfc_regs->e_rc_config);  	/** macroblock level rate control */  	reg &= ~(0x1 << 8);  	reg |= ((p->rc_mb & 0x1) << 8); -	WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6); +	WRITEL(reg, mfc_regs->e_rc_config);  	/* frame rate */  	if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {  		reg = 0;  		reg |= ((p->rc_framerate_num & 0xFFFF) << 16);  		reg |= p->rc_framerate_denom & 0xFFFF; -		WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6); +		WRITEL(reg, mfc_regs->e_rc_frame_rate); +	} + +	/* frame QP */ +	reg &= ~(0x7F); +	reg |= p_vp8->rc_frame_qp & 0x7F; +	WRITEL(reg, mfc_regs->e_rc_config); + +	/* other QPs */ +	WRITEL(0x0, mfc_regs->e_fixed_picture_qp); +	if (!p->rc_frame && !p->rc_mb) { +		reg = 0; +		reg |= ((p_vp8->rc_p_frame_qp & 0x7F) << 8); +		reg |= p_vp8->rc_frame_qp & 0x7F; +		WRITEL(reg, mfc_regs->e_fixed_picture_qp);  	} +	/* max QP */ +	reg = ((p_vp8->rc_max_qp & 0x7F) << 8); +	/* min QP */ +	reg |= p_vp8->rc_min_qp & 0x7F; +	WRITEL(reg, mfc_regs->e_rc_qp_bound); +  	/* vbv buffer size */  	if (p->frame_skip_mode ==  			V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) { -		WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); +		WRITEL(p->vbv_size & 0xFFFF, mfc_regs->e_vbv_buffer_size);  		if (p->rc_frame) -			WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); +			WRITEL(p->vbv_delay, mfc_regs->e_vbv_init_delay);  	}  	/* VP8 specific params */ @@ -1246,7 +1317,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)  	}  	reg |= (val & 0xF) << 3;  	reg |= (p_vp8->num_ref & 0x2); -	WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7); +	WRITEL(reg, mfc_regs->e_vp8_options);  	mfc_debug_leave(); @@ -1257,6 +1328,7 @@ static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	unsigned int reg = 0;  	int fmo_aso_ctrl = 0; @@ -1264,9 +1336,9 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)  	mfc_debug(2, "InstNo: %d/%d\n", ctx->inst_no,  			S5P_FIMV_CH_SEQ_HEADER_V6);  	mfc_debug(2, "BUFs: %08x %08x %08x\n", -		  READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6), -		  READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6), -		  READL(S5P_FIMV_D_CPB_BUFFER_ADDR_V6)); +		  READL(mfc_regs->d_cpb_buffer_addr), +		  READL(mfc_regs->d_cpb_buffer_addr), +		  READL(mfc_regs->d_cpb_buffer_addr));  	/* FMO_ASO_CTRL - 0: Enable, 1: Disable */  	reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6); @@ -1277,11 +1349,11 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)  	 * set to negative value. */  	if (ctx->display_delay >= 0) {  		reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6); -		WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6); +		WRITEL(ctx->display_delay, mfc_regs->d_display_delay);  	} -	if (IS_MFCV7(dev)) { -		WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); +	if (IS_MFCV7_PLUS(dev)) { +		WRITEL(reg, mfc_regs->d_dec_options);  		reg = 0;  	} @@ -1295,22 +1367,22 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)  	if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)  		reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6); -	if (IS_MFCV7(dev)) -		WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7); +	if (IS_MFCV7_PLUS(dev)) +		WRITEL(reg, mfc_regs->d_init_buffer_options);  	else -		WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6); +		WRITEL(reg, mfc_regs->d_dec_options);  	/* 0: NV12(CbCr), 1: NV21(CrCb) */  	if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M) -		WRITEL(0x1, S5P_FIMV_PIXEL_FORMAT_V6); +		WRITEL(0x1, mfc_regs->pixel_format);  	else -		WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6); +		WRITEL(0x0, mfc_regs->pixel_format);  	/* sei parse */ -	WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6); +	WRITEL(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable); -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  			S5P_FIMV_CH_SEQ_HEADER_V6, NULL); @@ -1321,11 +1393,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)  static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	if (flush) {  		dev->curr_ctx = ctx->num;  		s5p_mfc_clean_ctx_int_flags(ctx); -		WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +		WRITEL(ctx->inst_no, mfc_regs->instance_id);  		s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  				S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);  	} @@ -1336,11 +1409,12 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,  			enum s5p_mfc_decode_arg last_frame)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; -	WRITEL(ctx->dec_dst_flag, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6); -	WRITEL(ctx->slice_interface & 0x1, S5P_FIMV_D_SLICE_IF_ENABLE_V6); +	WRITEL(ctx->dec_dst_flag, mfc_regs->d_available_dpb_flag_lower); +	WRITEL(ctx->slice_interface & 0x1, mfc_regs->d_slice_if_enable); -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	/* Issue different commands to instance basing on whether it  	 * is the last frame or not. */  	switch (last_frame) { @@ -1364,6 +1438,7 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,  static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	if (ctx->codec_mode == S5P_MFC_CODEC_H264_ENC)  		s5p_mfc_set_enc_params_h264(ctx); @@ -1379,13 +1454,13 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)  		return -EINVAL;  	} -	/* Set stride lengths */ -	if (IS_MFCV7(dev)) { -		WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); -		WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); +	/* Set stride lengths for v7 & above */ +	if (IS_MFCV7_PLUS(dev)) { +		WRITEL(ctx->img_width, mfc_regs->e_source_first_plane_stride); +		WRITEL(ctx->img_width, mfc_regs->e_source_second_plane_stride);  	} -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  			S5P_FIMV_CH_SEQ_HEADER_V6, NULL); @@ -1395,14 +1470,16 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	struct s5p_mfc_enc_params *p = &ctx->enc_params;  	struct s5p_mfc_h264_enc_params *p_h264 = &p->codec.h264;  	int i;  	if (p_h264->aso) { -		for (i = 0; i < 8; i++) +		for (i = 0; i < ARRAY_SIZE(p_h264->aso_slice_order); i++) {  			WRITEL(p_h264->aso_slice_order[i], -				S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6 + i * 4); +				mfc_regs->e_h264_aso_slice_order_0 + i * 4); +		}  	}  	return 0;  } @@ -1411,6 +1488,7 @@ static int s5p_mfc_h264_set_aso_slice_order_v6(struct s5p_mfc_ctx *ctx)  static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)  {  	struct s5p_mfc_dev *dev = ctx->dev; +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;  	mfc_debug(2, "++\n"); @@ -1421,7 +1499,7 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)  	s5p_mfc_set_slice_mode(ctx); -	WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); +	WRITEL(ctx->inst_no, mfc_regs->instance_id);  	s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,  			S5P_FIMV_CH_FRAME_START_V6, NULL); @@ -1796,28 +1874,26 @@ static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)  static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)  { -	mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6); -	mfc_write(dev, 0, S5P_FIMV_RISC2HOST_INT_V6); +	const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs; +	WRITEL(0, mfc_regs->risc2host_command); +	WRITEL(0, mfc_regs->risc2host_int);  }  static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,  		unsigned int ofs)  { -	struct s5p_mfc_dev *dev = ctx->dev; -  	s5p_mfc_clock_on(); -	WRITEL(data, ofs); +	WRITEL(data, (void *)ofs);  	s5p_mfc_clock_off();  }  static unsigned int  s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)  { -	struct s5p_mfc_dev *dev = ctx->dev;  	int ret;  	s5p_mfc_clock_on(); -	ret = READL(ofs); +	ret = READL((void *)ofs);  	s5p_mfc_clock_off();  	return ret; @@ -1825,50 +1901,51 @@ s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned int ofs)  static int s5p_mfc_get_dspl_y_adr_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); +	return READL(dev->mfc_regs->d_display_first_plane_addr);  }  static int s5p_mfc_get_dec_y_adr_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); +	return READL(dev->mfc_regs->d_decoded_first_plane_addr);  }  static int s5p_mfc_get_dspl_status_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DISPLAY_STATUS_V6); +	return READL(dev->mfc_regs->d_display_status);  }  static int s5p_mfc_get_dec_status_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DECODED_STATUS_V6); +	return READL(dev->mfc_regs->d_decoded_status);  }  static int s5p_mfc_get_dec_frame_type_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DECODED_FRAME_TYPE_V6) & +	return READL(dev->mfc_regs->d_decoded_frame_type) &  		S5P_FIMV_DECODE_FRAME_MASK_V6;  }  static int s5p_mfc_get_disp_frame_type_v6(struct s5p_mfc_ctx *ctx)  { -	return mfc_read(ctx->dev, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6) & +	struct s5p_mfc_dev *dev = ctx->dev; +	return READL(dev->mfc_regs->d_display_frame_type) &  		S5P_FIMV_DECODE_FRAME_MASK_V6;  }  static int s5p_mfc_get_consumed_stream_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DECODED_NAL_SIZE_V6); +	return READL(dev->mfc_regs->d_decoded_nal_size);  }  static int s5p_mfc_get_int_reason_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_RISC2HOST_CMD_V6) & +	return READL(dev->mfc_regs->risc2host_command) &  		S5P_FIMV_RISC2HOST_CMD_MASK;  }  static int s5p_mfc_get_int_err_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_ERROR_CODE_V6); +	return READL(dev->mfc_regs->error_code);  }  static int s5p_mfc_err_dec_v6(unsigned int err) @@ -1883,82 +1960,323 @@ static int s5p_mfc_err_dspl_v6(unsigned int err)  static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); +	return READL(dev->mfc_regs->d_display_frame_width);  }  static int s5p_mfc_get_img_height_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); +	return READL(dev->mfc_regs->d_display_frame_height);  }  static int s5p_mfc_get_dpb_count_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_MIN_NUM_DPB_V6); +	return READL(dev->mfc_regs->d_min_num_dpb);  }  static int s5p_mfc_get_mv_count_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_MIN_NUM_MV_V6); +	return READL(dev->mfc_regs->d_min_num_mv);  }  static int s5p_mfc_get_inst_no_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_RET_INSTANCE_ID_V6); +	return READL(dev->mfc_regs->ret_instance_id);  }  static int s5p_mfc_get_enc_dpb_count_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_E_NUM_DPB_V6); +	return READL(dev->mfc_regs->e_num_dpb);  }  static int s5p_mfc_get_enc_strm_size_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_E_STREAM_SIZE_V6); +	return READL(dev->mfc_regs->e_stream_size);  }  static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_E_SLICE_TYPE_V6); +	return READL(dev->mfc_regs->e_slice_type);  }  static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_E_PICTURE_COUNT_V6); +	return READL(dev->mfc_regs->e_picture_count);  }  static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)  { -	return mfc_read(ctx->dev, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); +	struct s5p_mfc_dev *dev = ctx->dev; +	return READL(dev->mfc_regs->d_frame_pack_sei_avail);  }  static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_MVC_NUM_VIEWS_V6); +	return READL(dev->mfc_regs->d_mvc_num_views);  }  static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)  { -	return mfc_read(dev, S5P_FIMV_D_MVC_VIEW_ID_V6); +	return READL(dev->mfc_regs->d_mvc_view_id);  }  static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)  { -	return s5p_mfc_read_info_v6(ctx, PIC_TIME_TOP_V6); +	return s5p_mfc_read_info_v6(ctx, +		(unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_top);  }  static unsigned int s5p_mfc_get_pic_type_bot_v6(struct s5p_mfc_ctx *ctx)  { -	return s5p_mfc_read_info_v6(ctx, PIC_TIME_BOT_V6); +	return s5p_mfc_read_info_v6(ctx, +		(unsigned int) ctx->dev->mfc_regs->d_ret_picture_tag_bot);  }  static unsigned int s5p_mfc_get_crop_info_h_v6(struct s5p_mfc_ctx *ctx)  { -	return s5p_mfc_read_info_v6(ctx, CROP_INFO_H_V6); +	return s5p_mfc_read_info_v6(ctx, +		(unsigned int) ctx->dev->mfc_regs->d_display_crop_info1);  }  static unsigned int s5p_mfc_get_crop_info_v_v6(struct s5p_mfc_ctx *ctx)  { -	return s5p_mfc_read_info_v6(ctx, CROP_INFO_V_V6); +	return s5p_mfc_read_info_v6(ctx, +		(unsigned int) ctx->dev->mfc_regs->d_display_crop_info2); +} + +static struct s5p_mfc_regs mfc_regs; + +/* Initialize registers for MFC v6 onwards */ +const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev) +{ +	memset(&mfc_regs, 0, sizeof(mfc_regs)); + +#define S5P_MFC_REG_ADDR(dev, reg) ((dev)->regs_base + (reg)) +#define R(m, r) mfc_regs.m = S5P_MFC_REG_ADDR(dev, r) +	/* codec common registers */ +	R(risc_on, S5P_FIMV_RISC_ON_V6); +	R(risc2host_int, S5P_FIMV_RISC2HOST_INT_V6); +	R(host2risc_int, S5P_FIMV_HOST2RISC_INT_V6); +	R(risc_base_address, S5P_FIMV_RISC_BASE_ADDRESS_V6); +	R(mfc_reset, S5P_FIMV_MFC_RESET_V6); +	R(host2risc_command, S5P_FIMV_HOST2RISC_CMD_V6); +	R(risc2host_command, S5P_FIMV_RISC2HOST_CMD_V6); +	R(firmware_version, S5P_FIMV_FW_VERSION_V6); +	R(instance_id, S5P_FIMV_INSTANCE_ID_V6); +	R(codec_type, S5P_FIMV_CODEC_TYPE_V6); +	R(context_mem_addr, S5P_FIMV_CONTEXT_MEM_ADDR_V6); +	R(context_mem_size, S5P_FIMV_CONTEXT_MEM_SIZE_V6); +	R(pixel_format, S5P_FIMV_PIXEL_FORMAT_V6); +	R(ret_instance_id, S5P_FIMV_RET_INSTANCE_ID_V6); +	R(error_code, S5P_FIMV_ERROR_CODE_V6); + +	/* decoder registers */ +	R(d_crc_ctrl, S5P_FIMV_D_CRC_CTRL_V6); +	R(d_dec_options, S5P_FIMV_D_DEC_OPTIONS_V6); +	R(d_display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6); +	R(d_sei_enable, S5P_FIMV_D_SEI_ENABLE_V6); +	R(d_min_num_dpb, S5P_FIMV_D_MIN_NUM_DPB_V6); +	R(d_min_num_mv, S5P_FIMV_D_MIN_NUM_MV_V6); +	R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V6); +	R(d_num_dpb, S5P_FIMV_D_NUM_DPB_V6); +	R(d_num_mv, S5P_FIMV_D_NUM_MV_V6); +	R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V6); +	R(d_first_plane_dpb_size, S5P_FIMV_D_LUMA_DPB_SIZE_V6); +	R(d_second_plane_dpb_size, S5P_FIMV_D_CHROMA_DPB_SIZE_V6); +	R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V6); +	R(d_first_plane_dpb, S5P_FIMV_D_LUMA_DPB_V6); +	R(d_second_plane_dpb, S5P_FIMV_D_CHROMA_DPB_V6); +	R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V6); +	R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V6); +	R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V6); +	R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V6); +	R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V6); +	R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V6); +	R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V6); +	R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V6); +	R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V6); +	R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V6); +	R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V6); +	R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V6); +	R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_LUMA_ADDR_V6); +	R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_CHROMA_ADDR_V6); +	R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V6); +	R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V6); +	R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V6); +	R(d_display_aspect_ratio, S5P_FIMV_D_DISPLAY_ASPECT_RATIO_V6); +	R(d_display_extended_ar, S5P_FIMV_D_DISPLAY_EXTENDED_AR_V6); +	R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V6); +	R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_LUMA_ADDR_V6); +	R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_CHROMA_ADDR_V6); +	R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V6); +	R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V6); +	R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6); +	R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6); +	R(d_h264_info, S5P_FIMV_D_H264_INFO_V6); +	R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V6); +	R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V6); + +	/* encoder registers */ +	R(e_frame_width, S5P_FIMV_E_FRAME_WIDTH_V6); +	R(e_frame_height, S5P_FIMV_E_FRAME_HEIGHT_V6); +	R(e_cropped_frame_width, S5P_FIMV_E_CROPPED_FRAME_WIDTH_V6); +	R(e_cropped_frame_height, S5P_FIMV_E_CROPPED_FRAME_HEIGHT_V6); +	R(e_frame_crop_offset, S5P_FIMV_E_FRAME_CROP_OFFSET_V6); +	R(e_enc_options, S5P_FIMV_E_ENC_OPTIONS_V6); +	R(e_picture_profile, S5P_FIMV_E_PICTURE_PROFILE_V6); +	R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V6); +	R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6); +	R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V6); +	R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V6); +	R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V6); +	R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V6); +	R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V6); +	R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V6); +	R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V6); +	R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V6); +	R(e_num_dpb, S5P_FIMV_E_NUM_DPB_V6); +	R(e_luma_dpb, S5P_FIMV_E_LUMA_DPB_V6); +	R(e_chroma_dpb, S5P_FIMV_E_CHROMA_DPB_V6); +	R(e_me_buffer, S5P_FIMV_E_ME_BUFFER_V6); +	R(e_scratch_buffer_addr, S5P_FIMV_E_SCRATCH_BUFFER_ADDR_V6); +	R(e_scratch_buffer_size, S5P_FIMV_E_SCRATCH_BUFFER_SIZE_V6); +	R(e_tmv_buffer0, S5P_FIMV_E_TMV_BUFFER0_V6); +	R(e_tmv_buffer1, S5P_FIMV_E_TMV_BUFFER1_V6); +	R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); +	R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6); +	R(e_stream_buffer_addr, S5P_FIMV_E_STREAM_BUFFER_ADDR_V6); +	R(e_stream_buffer_size, S5P_FIMV_E_STREAM_BUFFER_SIZE_V6); +	R(e_roi_buffer_addr, S5P_FIMV_E_ROI_BUFFER_ADDR_V6); +	R(e_param_change, S5P_FIMV_E_PARAM_CHANGE_V6); +	R(e_ir_size, S5P_FIMV_E_IR_SIZE_V6); +	R(e_gop_config, S5P_FIMV_E_GOP_CONFIG_V6); +	R(e_mslice_mode, S5P_FIMV_E_MSLICE_MODE_V6); +	R(e_mslice_size_mb, S5P_FIMV_E_MSLICE_SIZE_MB_V6); +	R(e_mslice_size_bits, S5P_FIMV_E_MSLICE_SIZE_BITS_V6); +	R(e_frame_insertion, S5P_FIMV_E_FRAME_INSERTION_V6); +	R(e_rc_frame_rate, S5P_FIMV_E_RC_FRAME_RATE_V6); +	R(e_rc_bit_rate, S5P_FIMV_E_RC_BIT_RATE_V6); +	R(e_rc_roi_ctrl, S5P_FIMV_E_RC_ROI_CTRL_V6); +	R(e_picture_tag, S5P_FIMV_E_PICTURE_TAG_V6); +	R(e_bit_count_enable, S5P_FIMV_E_BIT_COUNT_ENABLE_V6); +	R(e_max_bit_count, S5P_FIMV_E_MAX_BIT_COUNT_V6); +	R(e_min_bit_count, S5P_FIMV_E_MIN_BIT_COUNT_V6); +	R(e_metadata_buffer_addr, S5P_FIMV_E_METADATA_BUFFER_ADDR_V6); +	R(e_metadata_buffer_size, S5P_FIMV_E_METADATA_BUFFER_SIZE_V6); +	R(e_encoded_source_first_plane_addr, +			S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6); +	R(e_encoded_source_second_plane_addr, +			S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6); +	R(e_stream_size, S5P_FIMV_E_STREAM_SIZE_V6); +	R(e_slice_type, S5P_FIMV_E_SLICE_TYPE_V6); +	R(e_picture_count, S5P_FIMV_E_PICTURE_COUNT_V6); +	R(e_ret_picture_tag, S5P_FIMV_E_RET_PICTURE_TAG_V6); +	R(e_recon_luma_dpb_addr, S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6); +	R(e_recon_chroma_dpb_addr, S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6); +	R(e_mpeg4_options, S5P_FIMV_E_MPEG4_OPTIONS_V6); +	R(e_mpeg4_hec_period, S5P_FIMV_E_MPEG4_HEC_PERIOD_V6); +	R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V6); +	R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V6); +	R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V6); +	R(e_h264_lf_alpha_offset, S5P_FIMV_E_H264_LF_ALPHA_OFFSET_V6); +	R(e_h264_lf_beta_offset, S5P_FIMV_E_H264_LF_BETA_OFFSET_V6); +	R(e_h264_i_period, S5P_FIMV_E_H264_I_PERIOD_V6); +	R(e_h264_fmo_slice_grp_map_type, +			S5P_FIMV_E_H264_FMO_SLICE_GRP_MAP_TYPE_V6); +	R(e_h264_fmo_num_slice_grp_minus1, +			S5P_FIMV_E_H264_FMO_NUM_SLICE_GRP_MINUS1_V6); +	R(e_h264_fmo_slice_grp_change_dir, +			S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_DIR_V6); +	R(e_h264_fmo_slice_grp_change_rate_minus1, +			S5P_FIMV_E_H264_FMO_SLICE_GRP_CHANGE_RATE_MINUS1_V6); +	R(e_h264_fmo_run_length_minus1_0, +			S5P_FIMV_E_H264_FMO_RUN_LENGTH_MINUS1_0_V6); +	R(e_h264_aso_slice_order_0, S5P_FIMV_E_H264_ASO_SLICE_ORDER_0_V6); +	R(e_h264_num_t_layer, S5P_FIMV_E_H264_NUM_T_LAYER_V6); +	R(e_h264_hierarchical_qp_layer0, +			S5P_FIMV_E_H264_HIERARCHICAL_QP_LAYER0_V6); +	R(e_h264_frame_packing_sei_info, +			S5P_FIMV_E_H264_FRAME_PACKING_SEI_INFO_V6); + +	if (!IS_MFCV7_PLUS(dev)) +		goto done; + +	/* Initialize registers used in MFC v7+ */ +	R(e_source_first_plane_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7); +	R(e_source_second_plane_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7); +	R(e_source_third_plane_addr, S5P_FIMV_E_SOURCE_THIRD_ADDR_V7); +	R(e_source_first_plane_stride, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7); +	R(e_source_second_plane_stride, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7); +	R(e_source_third_plane_stride, S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7); +	R(e_encoded_source_first_plane_addr, +			S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7); +	R(e_encoded_source_second_plane_addr, +			S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7); +	R(e_vp8_options, S5P_FIMV_E_VP8_OPTIONS_V7); + +	if (!IS_MFCV8(dev)) +		goto done; + +	/* Initialize registers used in MFC v8 only. +	 * Also, over-write the registers which have +	 * a different offset for MFC v8. */ +	R(d_stream_data_size, S5P_FIMV_D_STREAM_DATA_SIZE_V8); +	R(d_cpb_buffer_addr, S5P_FIMV_D_CPB_BUFFER_ADDR_V8); +	R(d_cpb_buffer_size, S5P_FIMV_D_CPB_BUFFER_SIZE_V8); +	R(d_cpb_buffer_offset, S5P_FIMV_D_CPB_BUFFER_OFFSET_V8); +	R(d_first_plane_dpb_size, S5P_FIMV_D_FIRST_PLANE_DPB_SIZE_V8); +	R(d_second_plane_dpb_size, S5P_FIMV_D_SECOND_PLANE_DPB_SIZE_V8); +	R(d_scratch_buffer_addr, S5P_FIMV_D_SCRATCH_BUFFER_ADDR_V8); +	R(d_scratch_buffer_size, S5P_FIMV_D_SCRATCH_BUFFER_SIZE_V8); +	R(d_first_plane_dpb_stride_size, +			S5P_FIMV_D_FIRST_PLANE_DPB_STRIDE_SIZE_V8); +	R(d_second_plane_dpb_stride_size, +			S5P_FIMV_D_SECOND_PLANE_DPB_STRIDE_SIZE_V8); +	R(d_mv_buffer_size, S5P_FIMV_D_MV_BUFFER_SIZE_V8); +	R(d_num_mv, S5P_FIMV_D_NUM_MV_V8); +	R(d_first_plane_dpb, S5P_FIMV_D_FIRST_PLANE_DPB_V8); +	R(d_second_plane_dpb, S5P_FIMV_D_SECOND_PLANE_DPB_V8); +	R(d_mv_buffer, S5P_FIMV_D_MV_BUFFER_V8); +	R(d_init_buffer_options, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V8); +	R(d_available_dpb_flag_lower, S5P_FIMV_D_AVAILABLE_DPB_FLAG_LOWER_V8); +	R(d_slice_if_enable, S5P_FIMV_D_SLICE_IF_ENABLE_V8); +	R(d_display_first_plane_addr, S5P_FIMV_D_DISPLAY_FIRST_PLANE_ADDR_V8); +	R(d_display_second_plane_addr, S5P_FIMV_D_DISPLAY_SECOND_PLANE_ADDR_V8); +	R(d_decoded_first_plane_addr, S5P_FIMV_D_DECODED_FIRST_PLANE_ADDR_V8); +	R(d_decoded_second_plane_addr, S5P_FIMV_D_DECODED_SECOND_PLANE_ADDR_V8); +	R(d_display_status, S5P_FIMV_D_DISPLAY_STATUS_V8); +	R(d_decoded_status, S5P_FIMV_D_DECODED_STATUS_V8); +	R(d_decoded_frame_type, S5P_FIMV_D_DECODED_FRAME_TYPE_V8); +	R(d_display_frame_type, S5P_FIMV_D_DISPLAY_FRAME_TYPE_V8); +	R(d_decoded_nal_size, S5P_FIMV_D_DECODED_NAL_SIZE_V8); +	R(d_display_frame_width, S5P_FIMV_D_DISPLAY_FRAME_WIDTH_V8); +	R(d_display_frame_height, S5P_FIMV_D_DISPLAY_FRAME_HEIGHT_V8); +	R(d_frame_pack_sei_avail, S5P_FIMV_D_FRAME_PACK_SEI_AVAIL_V8); +	R(d_mvc_num_views, S5P_FIMV_D_MVC_NUM_VIEWS_V8); +	R(d_mvc_view_id, S5P_FIMV_D_MVC_VIEW_ID_V8); +	R(d_ret_picture_tag_top, S5P_FIMV_D_RET_PICTURE_TAG_TOP_V8); +	R(d_ret_picture_tag_bot, S5P_FIMV_D_RET_PICTURE_TAG_BOT_V8); +	R(d_display_crop_info1, S5P_FIMV_D_DISPLAY_CROP_INFO1_V8); +	R(d_display_crop_info2, S5P_FIMV_D_DISPLAY_CROP_INFO2_V8); + +	/* encoder registers */ +	R(e_padding_ctrl, S5P_FIMV_E_PADDING_CTRL_V8); +	R(e_rc_config, S5P_FIMV_E_RC_CONFIG_V8); +	R(e_rc_mode, S5P_FIMV_E_RC_RPARAM_V8); +	R(e_mv_hor_range, S5P_FIMV_E_MV_HOR_RANGE_V8); +	R(e_mv_ver_range, S5P_FIMV_E_MV_VER_RANGE_V8); +	R(e_rc_qp_bound, S5P_FIMV_E_RC_QP_BOUND_V8); +	R(e_fixed_picture_qp, S5P_FIMV_E_FIXED_PICTURE_QP_V8); +	R(e_vbv_buffer_size, S5P_FIMV_E_VBV_BUFFER_SIZE_V8); +	R(e_vbv_init_delay, S5P_FIMV_E_VBV_INIT_DELAY_V8); +	R(e_mb_rc_config, S5P_FIMV_E_MB_RC_CONFIG_V8); +	R(e_aspect_ratio, S5P_FIMV_E_ASPECT_RATIO_V8); +	R(e_extended_sar, S5P_FIMV_E_EXTENDED_SAR_V8); +	R(e_h264_options, S5P_FIMV_E_H264_OPTIONS_V8); + +done: +	return &mfc_regs; +#undef S5P_MFC_REG_ADDR +#undef R  }  /* Initialize opr function pointers for MFC v6 */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h index ab164efa127..80558484bb4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h @@ -40,11 +40,6 @@  #define FRAME_DELTA_H264_H263		1  #define TIGHT_CBR_MAX			10 -/* Definitions for shared memory compatibility */ -#define PIC_TIME_TOP_V6		S5P_FIMV_D_RET_PICTURE_TAG_TOP_V6 -#define PIC_TIME_BOT_V6		S5P_FIMV_D_RET_PICTURE_TAG_BOT_V6 -#define CROP_INFO_H_V6		S5P_FIMV_D_DISPLAY_CROP_INFO1_V6 -#define CROP_INFO_V_V6		S5P_FIMV_D_DISPLAY_CROP_INFO2_V6 -  struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v6(void); +const struct s5p_mfc_regs *s5p_mfc_init_regs_v6_plus(struct s5p_mfc_dev *dev);  #endif /* S5P_MFC_OPR_V6_H_ */ diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 534722c04ec..754740f4b67 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -674,6 +674,8 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,  static int hdmi_enum_dv_timings(struct v4l2_subdev *sd,  	struct v4l2_enum_dv_timings *timings)  { +	if (timings->pad != 0) +		return -EINVAL;  	if (timings->index >= ARRAY_SIZE(hdmi_timings))  		return -EINVAL;  	timings->timings = hdmi_timings[timings->index].dv_timings; @@ -687,8 +689,11 @@ static int hdmi_dv_timings_cap(struct v4l2_subdev *sd,  {  	struct hdmi_device *hdev = sd_to_hdmi_dev(sd); +	if (cap->pad != 0) +		return -EINVAL; +  	/* Let the phy fill in the pixelclock range */ -	v4l2_subdev_call(hdev->phy_sd, video, dv_timings_cap, cap); +	v4l2_subdev_call(hdev->phy_sd, pad, dv_timings_cap, cap);  	cap->type = V4L2_DV_BT_656_1120;  	cap->bt.min_width = 720;  	cap->bt.max_width = 1920; @@ -707,12 +712,15 @@ static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {  static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {  	.s_dv_timings = hdmi_s_dv_timings,  	.g_dv_timings = hdmi_g_dv_timings, -	.enum_dv_timings = hdmi_enum_dv_timings, -	.dv_timings_cap = hdmi_dv_timings_cap,  	.g_mbus_fmt = hdmi_g_mbus_fmt,  	.s_stream = hdmi_s_stream,  }; +static const struct v4l2_subdev_pad_ops hdmi_sd_pad_ops = { +	.enum_dv_timings = hdmi_enum_dv_timings, +	.dv_timings_cap = hdmi_dv_timings_cap, +}; +  static const struct v4l2_subdev_ops hdmi_sd_ops = {  	.core = &hdmi_sd_core_ops,  	.video = &hdmi_sd_video_ops, diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index e19a0af1ea4..c2f2e35642f 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -225,6 +225,9 @@ static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,  static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,  	struct v4l2_dv_timings_cap *cap)  { +	if (cap->pad != 0) +		return -EINVAL; +  	cap->type = V4L2_DV_BT_656_1120;  	/* The phy only determines the pixelclock, leave the other values  	 * at 0 to signify that we have no information for them. */ @@ -259,13 +262,17 @@ static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {  static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {  	.s_dv_timings = hdmiphy_s_dv_timings, -	.dv_timings_cap = hdmiphy_dv_timings_cap,  	.s_stream =  hdmiphy_s_stream,  }; +static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = { +	.dv_timings_cap = hdmiphy_dv_timings_cap, +}; +  static const struct v4l2_subdev_ops hdmiphy_ops = {  	.core = &hdmiphy_core_ops,  	.video = &hdmiphy_video_ops, +	.pad = &hdmiphy_pad_ops,  };  static int hdmiphy_probe(struct i2c_client *client, diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index 04e6490a45b..fb2acc53112 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -65,7 +65,7 @@ struct mxr_format {  	int num_subframes;  	/** specifies to which subframe belong given plane */  	int plane2subframe[MXR_MAX_PLANES]; -	/** internal code, driver dependant */ +	/** internal code, driver dependent */  	unsigned long cookie;  }; diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index 51805a5e2be..bc08b5f28e4 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -347,19 +347,41 @@ static int mxr_runtime_resume(struct device *dev)  {  	struct mxr_device *mdev = to_mdev(dev);  	struct mxr_resources *res = &mdev->res; +	int ret;  	mxr_dbg(mdev, "resume - start\n");  	mutex_lock(&mdev->mutex);  	/* turn clocks on */ -	clk_enable(res->mixer); -	clk_enable(res->vp); -	clk_enable(res->sclk_mixer); +	ret = clk_prepare_enable(res->mixer); +	if (ret < 0) { +		dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n"); +		goto fail; +	} +	ret = clk_prepare_enable(res->vp); +	if (ret < 0) { +		dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n"); +		goto fail_mixer; +	} +	ret = clk_prepare_enable(res->sclk_mixer); +	if (ret < 0) { +		dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n"); +		goto fail_vp; +	}  	/* apply default configuration */  	mxr_reg_reset(mdev);  	mxr_dbg(mdev, "resume - finished\n");  	mutex_unlock(&mdev->mutex);  	return 0; + +fail_vp: +	clk_disable_unprepare(res->vp); +fail_mixer: +	clk_disable_unprepare(res->mixer); +fail: +	mutex_unlock(&mdev->mutex); +	dev_err(mdev->dev, "resume failed\n"); +	return ret;  }  static int mxr_runtime_suspend(struct device *dev) @@ -369,9 +391,9 @@ static int mxr_runtime_suspend(struct device *dev)  	mxr_dbg(mdev, "suspend - start\n");  	mutex_lock(&mdev->mutex);  	/* turn clocks off */ -	clk_disable(res->sclk_mixer); -	clk_disable(res->vp); -	clk_disable(res->mixer); +	clk_disable_unprepare(res->sclk_mixer); +	clk_disable_unprepare(res->vp); +	clk_disable_unprepare(res->mixer);  	mutex_unlock(&mdev->mutex);  	mxr_dbg(mdev, "suspend - finished\n");  	return 0; diff --git a/drivers/media/platform/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c index b93a21f5aa1..74344c764da 100644 --- a/drivers/media/platform/s5p-tv/mixer_grp_layer.c +++ b/drivers/media/platform/s5p-tv/mixer_grp_layer.c @@ -226,7 +226,7 @@ static void mxr_graph_fix_geometry(struct mxr_layer *layer,  			src->width + src->x_offset, 32767);  		src->full_height = clamp_val(src->full_height,  			src->height + src->y_offset, 2047); -	}; +	}  }  /* PUBLIC API */ diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 641b1f071e0..8a8dbc8fdfd 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -509,9 +509,11 @@ static int mxr_enum_dv_timings(struct file *file, void *fh,  	struct mxr_device *mdev = layer->mdev;  	int ret; +	timings->pad = 0; +  	/* lock protects from changing sd_out */  	mutex_lock(&mdev->mutex); -	ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_timings, timings); +	ret = v4l2_subdev_call(to_outsd(mdev), pad, enum_dv_timings, timings);  	mutex_unlock(&mdev->mutex);  	return ret ? -EINVAL : 0; @@ -528,7 +530,7 @@ static int mxr_s_dv_timings(struct file *file, void *fh,  	mutex_lock(&mdev->mutex);  	/* timings change cannot be done while there is an entity -	 * dependant on output configuration +	 * dependent on output configuration  	 */  	if (mdev->n_output > 0) {  		mutex_unlock(&mdev->mutex); @@ -567,9 +569,11 @@ static int mxr_dv_timings_cap(struct file *file, void *fh,  	struct mxr_device *mdev = layer->mdev;  	int ret; +	cap->pad = 0; +  	/* lock protects from changing sd_out */  	mutex_lock(&mdev->mutex); -	ret = v4l2_subdev_call(to_outsd(mdev), video, dv_timings_cap, cap); +	ret = v4l2_subdev_call(to_outsd(mdev), pad, dv_timings_cap, cap);  	mutex_unlock(&mdev->mutex);  	return ret ? -EINVAL : 0; @@ -585,7 +589,7 @@ static int mxr_s_std(struct file *file, void *fh, v4l2_std_id norm)  	mutex_lock(&mdev->mutex);  	/* standard change cannot be done while there is an entity -	 * dependant on output configuration +	 * dependent on output configuration  	 */  	if (mdev->n_output > 0) {  		mutex_unlock(&mdev->mutex); @@ -946,11 +950,6 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)  	mxr_dbg(mdev, "%s\n", __func__); -	if (count == 0) { -		mxr_dbg(mdev, "no output buffers queued\n"); -		return -EINVAL; -	} -  	/* block any changes in output configuration */  	mxr_output_get(mdev); @@ -990,7 +989,7 @@ static void mxr_watchdog(unsigned long arg)  	spin_unlock_irqrestore(&layer->enq_slock, flags);  } -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq)  {  	struct mxr_layer *layer = vb2_get_drv_priv(vq);  	struct mxr_device *mdev = layer->mdev; @@ -1036,7 +1035,6 @@ static int stop_streaming(struct vb2_queue *vq)  	mxr_streamer_put(mdev);  	/* allow changes in output configuration */  	mxr_output_put(mdev); -	return 0;  }  static struct vb2_ops mxr_video_qops = { @@ -1124,6 +1122,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,  		.drv_priv = layer,  		.buf_struct_size = sizeof(struct mxr_buffer),  		.ops = &mxr_video_qops, +		.min_buffers_needed = 1,  		.mem_ops = &vb2_dma_contig_memops,  	}; diff --git a/drivers/media/platform/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c index 3d13a636877..c9388c45ad7 100644 --- a/drivers/media/platform/s5p-tv/mixer_vp_layer.c +++ b/drivers/media/platform/s5p-tv/mixer_vp_layer.c @@ -197,7 +197,7 @@ static void mxr_vp_fix_geometry(struct mxr_layer *layer,  			ALIGN(src->width + src->x_offset, 8), 8192U);  		src->full_height = clamp(src->full_height,  			src->height + src->y_offset, 8192U); -	}; +	}  }  /* PUBLIC API */ diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 0afa90f0f6a..5a7c3796f22 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -55,6 +55,8 @@ struct sdo_device {  	struct clk *dacphy;  	/** clock for control of VPLL */  	struct clk *fout_vpll; +	/** vpll rate before sdo stream was on */ +	unsigned long vpll_rate;  	/** regulator for SDO IP power */  	struct regulator *vdac;  	/** regulator for SDO plug detection */ @@ -193,17 +195,33 @@ static int sdo_s_power(struct v4l2_subdev *sd, int on)  static int sdo_streamon(struct sdo_device *sdev)  { +	int ret; +  	/* set proper clock for Timing Generator */ -	clk_set_rate(sdev->fout_vpll, 54000000); +	sdev->vpll_rate = clk_get_rate(sdev->fout_vpll); +	ret = clk_set_rate(sdev->fout_vpll, 54000000); +	if (ret < 0) { +		dev_err(sdev->dev, "Failed to set vpll rate\n"); +		return ret; +	}  	dev_info(sdev->dev, "fout_vpll.rate = %lu\n",  	clk_get_rate(sdev->fout_vpll));  	/* enable clock in SDO */  	sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON); -	clk_enable(sdev->dacphy); +	ret = clk_prepare_enable(sdev->dacphy); +	if (ret < 0) { +		dev_err(sdev->dev, "clk_prepare_enable(dacphy) failed\n"); +		goto fail; +	}  	/* enable DAC */  	sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);  	sdo_reg_debug(sdev);  	return 0; + +fail: +	sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); +	clk_set_rate(sdev->fout_vpll, sdev->vpll_rate); +	return ret;  }  static int sdo_streamoff(struct sdo_device *sdev) @@ -211,7 +229,7 @@ static int sdo_streamoff(struct sdo_device *sdev)  	int tries;  	sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC); -	clk_disable(sdev->dacphy); +	clk_disable_unprepare(sdev->dacphy);  	sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);  	for (tries = 100; tries; --tries) {  		if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY) @@ -220,6 +238,7 @@ static int sdo_streamoff(struct sdo_device *sdev)  	}  	if (tries == 0)  		dev_err(sdev->dev, "failed to stop streaming\n"); +	clk_set_rate(sdev->fout_vpll, sdev->vpll_rate);  	return tries ? 0 : -EIO;  } @@ -254,7 +273,7 @@ static int sdo_runtime_suspend(struct device *dev)  	dev_info(dev, "suspend\n");  	regulator_disable(sdev->vdet);  	regulator_disable(sdev->vdac); -	clk_disable(sdev->sclk_dac); +	clk_disable_unprepare(sdev->sclk_dac);  	return 0;  } @@ -266,7 +285,7 @@ static int sdo_runtime_resume(struct device *dev)  	dev_info(dev, "resume\n"); -	ret = clk_enable(sdev->sclk_dac); +	ret = clk_prepare_enable(sdev->sclk_dac);  	if (ret < 0)  		return ret; @@ -299,7 +318,7 @@ static int sdo_runtime_resume(struct device *dev)  vdac_r_dis:  	regulator_disable(sdev->vdac);  dac_clk_dis: -	clk_disable(sdev->sclk_dac); +	clk_disable_unprepare(sdev->sclk_dac);  	return ret;  } @@ -405,7 +424,11 @@ static int sdo_probe(struct platform_device *pdev)  	}  	/* enable gate for dac clock, because mixer uses it */ -	clk_enable(sdev->dac); +	ret = clk_prepare_enable(sdev->dac); +	if (ret < 0) { +		dev_err(dev, "clk_prepare_enable(dac) failed\n"); +		goto fail_fout_vpll; +	}  	/* configure power management */  	pm_runtime_enable(dev); @@ -444,7 +467,7 @@ static int sdo_remove(struct platform_device *pdev)  	struct sdo_device *sdev = sd_to_sdev(sd);  	pm_runtime_disable(&pdev->dev); -	clk_disable(sdev->dac); +	clk_disable_unprepare(sdev->dac);  	clk_put(sdev->fout_vpll);  	clk_put(sdev->dacphy);  	clk_put(sdev->dac); diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 7a9c5e9329f..e5f1d4c14f2 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -286,7 +286,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq,  	vb->size = vb->height * bytes_per_line;  	if (vb->baddr && vb->bsize < vb->size) {  		/* User buffer too small */ -		dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n", +		dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n",  			 vb->bsize, vb->baddr);  		return -EINVAL;  	} @@ -302,9 +302,10 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq,  	}  	dev_dbg(vou_dev->v4l2_dev.dev, -		"%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", +		"%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n",  		__func__, vou_dev->pix_idx, bytes_per_line, -		videobuf_to_dma_contig(vb), vb->memory, vb->state); +		({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }), +		vb->memory, vb->state);  	return 0;  } @@ -442,7 +443,7 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,  				      int pix_idx, int w_idx, int h_idx)  {  	struct sh_vou_fmt *fmt = vou_fmt + pix_idx; -	unsigned int black_left, black_top, width_max, height_max, +	unsigned int black_left, black_top, width_max,  		frame_in_height, frame_out_height, frame_out_top;  	struct v4l2_rect *rect = &vou_dev->rect;  	struct v4l2_pix_format *pix = &vou_dev->pix; @@ -450,10 +451,10 @@ static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,  	if (vou_dev->std & V4L2_STD_525_60) {  		width_max = 858; -		height_max = 262; +		/* height_max = 262; */  	} else {  		width_max = 864; -		height_max = 312; +		/* height_max = 312; */  	}  	frame_in_height = pix->height / 2; @@ -776,7 +777,7 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,  	v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,  			      &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); -	for (i = 0; ARRAY_SIZE(vou_fmt); i++) +	for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)  		if (vou_fmt[i].pfmt == pix->pixelformat)  			return 0; @@ -1052,7 +1053,6 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)  	static unsigned long j;  	struct videobuf_buffer *vb;  	static int cnt; -	static int side;  	u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;  	u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR); @@ -1080,7 +1080,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)  		irq_status, masked, vou_status, cnt);  	cnt++; -	side = vou_status & 0x10000; +	/* side = vou_status & 0x10000; */  	/* Clear only set interrupts */  	sh_vou_reg_a_write(vou_dev, VOUIR, masked); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 10448563250..38c723aca43 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -34,13 +34,6 @@  #define MIN_FRAME_RATE			15  #define FRAME_INTERVAL_MILLI_SEC	(1000 / MIN_FRAME_RATE) -/* ISI states */ -enum { -	ISI_STATE_IDLE = 0, -	ISI_STATE_READY, -	ISI_STATE_WAIT_SOF, -}; -  /* Frame buffer descriptor */  struct fbd {  	/* Physical address of the frame buffer */ @@ -75,11 +68,6 @@ struct atmel_isi {  	void __iomem			*regs;  	int				sequence; -	/* State of the ISI module in capturing mode */ -	int				state; - -	/* Wait queue for waiting for SOF */ -	wait_queue_head_t		vsync_wq;  	struct vb2_alloc_ctx		*alloc_ctx; @@ -124,16 +112,16 @@ static int configure_geometry(struct atmel_isi *isi, u32 width,  	case V4L2_MBUS_FMT_Y8_1X8:  		cr = ISI_CFG2_GRAYSCALE;  		break; -	case V4L2_MBUS_FMT_UYVY8_2X8: +	case V4L2_MBUS_FMT_VYUY8_2X8:  		cr = ISI_CFG2_YCC_SWAP_MODE_3;  		break; -	case V4L2_MBUS_FMT_VYUY8_2X8: +	case V4L2_MBUS_FMT_UYVY8_2X8:  		cr = ISI_CFG2_YCC_SWAP_MODE_2;  		break; -	case V4L2_MBUS_FMT_YUYV8_2X8: +	case V4L2_MBUS_FMT_YVYU8_2X8:  		cr = ISI_CFG2_YCC_SWAP_MODE_1;  		break; -	case V4L2_MBUS_FMT_YVYU8_2X8: +	case V4L2_MBUS_FMT_YUYV8_2X8:  		cr = ISI_CFG2_YCC_SWAP_DEFAULT;  		break;  	/* RGB, TODO */ @@ -144,6 +132,8 @@ static int configure_geometry(struct atmel_isi *isi, u32 width,  	isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);  	cfg2 = isi_readl(isi, ISI_CFG2); +	/* Set YCC swap mode */ +	cfg2 &= ~ISI_CFG2_YCC_SWAP_MODE_MASK;  	cfg2 |= cr;  	/* Set width */  	cfg2 &= ~(ISI_CFG2_IM_HSIZE_MASK); @@ -207,12 +197,6 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id)  		isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);  		ret = IRQ_HANDLED;  	} else { -		if ((pending & ISI_SR_VSYNC) && -				(isi->state == ISI_STATE_IDLE)) { -			isi->state = ISI_STATE_READY; -			wake_up_interruptible(&isi->vsync_wq); -			ret = IRQ_HANDLED; -		}  		if (likely(pending & ISI_SR_CXFR_DONE))  			ret = atmel_isi_handle_streaming(isi);  	} @@ -259,16 +243,6 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);  	struct atmel_isi *isi = ici->priv;  	unsigned long size; -	int ret; - -	/* Reset ISI */ -	ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); -	if (ret < 0) { -		dev_err(icd->parent, "Reset ISI timed out\n"); -		return ret; -	} -	/* Disable all interrupts */ -	isi_writel(isi, ISI_INTDIS, ~0UL);  	size = icd->sizeimage; @@ -374,6 +348,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)  	isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);  	isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH); +	cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK;  	/* Enable linked list */  	cfg1 |= isi->pdata->frate | ISI_CFG1_DISCR; @@ -407,47 +382,31 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)  	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent);  	struct atmel_isi *isi = ici->priv; -  	u32 sr = 0;  	int ret; -	spin_lock_irq(&isi->lock); -	isi->state = ISI_STATE_IDLE; -	/* Clear any pending SOF interrupt */ -	sr = isi_readl(isi, ISI_STATUS); -	/* Enable VSYNC interrupt for SOF */ -	isi_writel(isi, ISI_INTEN, ISI_SR_VSYNC); -	isi_writel(isi, ISI_CTRL, ISI_CTRL_EN); -	spin_unlock_irq(&isi->lock); - -	dev_dbg(icd->parent, "Waiting for SOF\n"); -	ret = wait_event_interruptible(isi->vsync_wq, -				       isi->state != ISI_STATE_IDLE); -	if (ret) -		goto err; - -	if (isi->state != ISI_STATE_READY) { -		ret = -EIO; -		goto err; +	/* Reset ISI */ +	ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); +	if (ret < 0) { +		dev_err(icd->parent, "Reset ISI timed out\n"); +		return ret;  	} +	/* Disable all interrupts */ +	isi_writel(isi, ISI_INTDIS, ~0UL);  	spin_lock_irq(&isi->lock); -	isi->state = ISI_STATE_WAIT_SOF; -	isi_writel(isi, ISI_INTDIS, ISI_SR_VSYNC); +	/* Clear any pending interrupt */ +	sr = isi_readl(isi, ISI_STATUS); +  	if (count)  		start_dma(isi, isi->active);  	spin_unlock_irq(&isi->lock);  	return 0; -err: -	isi->active = NULL; -	isi->sequence = 0; -	INIT_LIST_HEAD(&isi->video_buffer_list); -	return ret;  }  /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq)  {  	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -474,7 +433,7 @@ static int stop_streaming(struct vb2_queue *vq)  	if (time_after(jiffies, timeout)) {  		dev_err(icd->parent,  			"Timeout waiting for finishing codec request\n"); -		return -ETIMEDOUT; +		return;  	}  	/* Disable interrupts */ @@ -485,8 +444,6 @@ static int stop_streaming(struct vb2_queue *vq)  	ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);  	if (ret < 0)  		dev_err(icd->parent, "Disable ISI timed out\n"); - -	return ret;  }  static struct vb2_ops isi_video_qops = { @@ -513,7 +470,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q,  	q->buf_struct_size = sizeof(struct frame_buffer);  	q->ops = &isi_video_qops;  	q->mem_ops = &vb2_dma_contig_memops; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	return vb2_queue_init(q);  } @@ -765,14 +722,16 @@ static int isi_camera_clock_start(struct soc_camera_host *ici)  	struct atmel_isi *isi = ici->priv;  	int ret; -	ret = clk_enable(isi->pclk); +	ret = clk_prepare_enable(isi->pclk);  	if (ret)  		return ret; -	ret = clk_enable(isi->mck); -	if (ret) { -		clk_disable(isi->pclk); -		return ret; +	if (!IS_ERR(isi->mck)) { +		ret = clk_prepare_enable(isi->mck); +		if (ret) { +			clk_disable_unprepare(isi->pclk); +			return ret; +		}  	}  	return 0; @@ -783,8 +742,9 @@ static void isi_camera_clock_stop(struct soc_camera_host *ici)  {  	struct atmel_isi *isi = ici->priv; -	clk_disable(isi->mck); -	clk_disable(isi->pclk); +	if (!IS_ERR(isi->mck)) +		clk_disable_unprepare(isi->mck); +	clk_disable_unprepare(isi->pclk);  }  static unsigned int isi_camera_poll(struct file *file, poll_table *pt) @@ -906,7 +866,6 @@ static int atmel_isi_remove(struct platform_device *pdev)  	struct atmel_isi *isi = container_of(soc_host,  					struct atmel_isi, soc_host); -	free_irq(isi->irq, isi);  	soc_camera_host_unregister(soc_host);  	vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);  	dma_free_coherent(&pdev->dev, @@ -914,13 +873,6 @@ static int atmel_isi_remove(struct platform_device *pdev)  			isi->p_fb_descriptors,  			isi->fb_descriptors_phys); -	iounmap(isi->regs); -	clk_unprepare(isi->mck); -	clk_put(isi->mck); -	clk_unprepare(isi->pclk); -	clk_put(isi->pclk); -	kfree(isi); -  	return 0;  } @@ -928,7 +880,6 @@ static int atmel_isi_probe(struct platform_device *pdev)  {  	unsigned int irq;  	struct atmel_isi *isi; -	struct clk *pclk;  	struct resource *regs;  	int ret, i;  	struct device *dev = &pdev->dev; @@ -936,64 +887,50 @@ static int atmel_isi_probe(struct platform_device *pdev)  	struct isi_platform_data *pdata;  	pdata = dev->platform_data; -	if (!pdata || !pdata->data_width_flags || !pdata->mck_hz) { +	if (!pdata || !pdata->data_width_flags) {  		dev_err(&pdev->dev,  			"No config available for Atmel ISI\n");  		return -EINVAL;  	} -	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!regs) -		return -ENXIO; - -	pclk = clk_get(&pdev->dev, "isi_clk"); -	if (IS_ERR(pclk)) -		return PTR_ERR(pclk); - -	ret = clk_prepare(pclk); -	if (ret) -		goto err_clk_prepare_pclk; - -	isi = kzalloc(sizeof(struct atmel_isi), GFP_KERNEL); +	isi = devm_kzalloc(&pdev->dev, sizeof(struct atmel_isi), GFP_KERNEL);  	if (!isi) { -		ret = -ENOMEM;  		dev_err(&pdev->dev, "Can't allocate interface!\n"); -		goto err_alloc_isi; +		return -ENOMEM;  	} -	isi->pclk = pclk; +	isi->pclk = devm_clk_get(&pdev->dev, "isi_clk"); +	if (IS_ERR(isi->pclk)) +		return PTR_ERR(isi->pclk); +  	isi->pdata = pdata;  	isi->active = NULL;  	spin_lock_init(&isi->lock); -	init_waitqueue_head(&isi->vsync_wq);  	INIT_LIST_HEAD(&isi->video_buffer_list);  	INIT_LIST_HEAD(&isi->dma_desc_head); -	/* Get ISI_MCK, provided by programmable clock or external clock */ -	isi->mck = clk_get(dev, "isi_mck"); -	if (IS_ERR(isi->mck)) { -		dev_err(dev, "Failed to get isi_mck\n"); -		ret = PTR_ERR(isi->mck); -		goto err_clk_get; +	/* ISI_MCK is the sensor master clock. It should be handled by the +	 * sensor driver directly, as the ISI has no use for that clock. Make +	 * the clock optional here while platforms transition to the correct +	 * model. +	 */ +	isi->mck = devm_clk_get(dev, "isi_mck"); +	if (!IS_ERR(isi->mck)) { +		/* Set ISI_MCK's frequency, it should be faster than pixel +		 * clock. +		 */ +		ret = clk_set_rate(isi->mck, pdata->mck_hz); +		if (ret < 0) +			return ret;  	} -	ret = clk_prepare(isi->mck); -	if (ret) -		goto err_clk_prepare_mck; - -	/* Set ISI_MCK's frequency, it should be faster than pixel clock */ -	ret = clk_set_rate(isi->mck, pdata->mck_hz); -	if (ret < 0) -		goto err_set_mck_rate; -  	isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,  				sizeof(struct fbd) * MAX_BUFFER_NUM,  				&isi->fb_descriptors_phys,  				GFP_KERNEL);  	if (!isi->p_fb_descriptors) { -		ret = -ENOMEM;  		dev_err(&pdev->dev, "Can't allocate descriptors!\n"); -		goto err_alloc_descriptors; +		return -ENOMEM;  	}  	for (i = 0; i < MAX_BUFFER_NUM; i++) { @@ -1009,9 +946,10 @@ static int atmel_isi_probe(struct platform_device *pdev)  		goto err_alloc_ctx;  	} -	isi->regs = ioremap(regs->start, resource_size(regs)); -	if (!isi->regs) { -		ret = -ENOMEM; +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	isi->regs = devm_ioremap_resource(&pdev->dev, regs); +	if (IS_ERR(isi->regs)) { +		ret = PTR_ERR(isi->regs);  		goto err_ioremap;  	} @@ -1028,7 +966,7 @@ static int atmel_isi_probe(struct platform_device *pdev)  		goto err_req_irq;  	} -	ret = request_irq(irq, isi_interrupt, 0, "isi", isi); +	ret = devm_request_irq(&pdev->dev, irq, isi_interrupt, 0, "isi", isi);  	if (ret) {  		dev_err(&pdev->dev, "Unable to request irq %d\n", irq);  		goto err_req_irq; @@ -1050,9 +988,7 @@ static int atmel_isi_probe(struct platform_device *pdev)  	return 0;  err_register_soc_camera_host: -	free_irq(isi->irq, isi);  err_req_irq: -	iounmap(isi->regs);  err_ioremap:  	vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);  err_alloc_ctx: @@ -1060,17 +996,6 @@ err_alloc_ctx:  			sizeof(struct fbd) * MAX_BUFFER_NUM,  			isi->p_fb_descriptors,  			isi->fb_descriptors_phys); -err_alloc_descriptors: -err_set_mck_rate: -	clk_unprepare(isi->mck); -err_clk_prepare_mck: -	clk_put(isi->mck); -err_clk_get: -	kfree(isi); -err_alloc_isi: -	clk_unprepare(pclk); -err_clk_prepare_pclk: -	clk_put(pclk);  	return ret;  } diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 45a0276be4e..b40bc2e5ba4 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -659,7 +659,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)  	unsigned long flags;  	if (count < 2) -		return -EINVAL; +		return -ENOBUFS;  	spin_lock_irqsave(&pcdev->lock, flags); @@ -741,7 +741,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)  	return 0;  } -static int mx2_stop_streaming(struct vb2_queue *q) +static void mx2_stop_streaming(struct vb2_queue *q)  {  	struct soc_camera_device *icd = soc_camera_from_vb2q(q);  	struct soc_camera_host *ici = @@ -773,8 +773,6 @@ static int mx2_stop_streaming(struct vb2_queue *q)  	dma_free_coherent(ici->v4l2_dev.dev,  			  pcdev->discard_size, b, pcdev->discard_buffer_dma); - -	return 0;  }  static struct vb2_ops mx2_videobuf_ops = { @@ -794,7 +792,7 @@ static int mx2_camera_init_videobuf(struct vb2_queue *q,  	q->ops = &mx2_videobuf_ops;  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct mx2_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	return vb2_queue_init(q);  } diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 8f9f6211c52..83315dfeef6 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -266,7 +266,6 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)  	struct idmac_channel *ichan = mx3_cam->idmac_channel[0];  	struct idmac_video_param *video = &ichan->params.video;  	const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; -	unsigned long flags;  	dma_cookie_t cookie;  	size_t new_size; @@ -328,7 +327,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)  		memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));  #endif -	spin_lock_irqsave(&mx3_cam->lock, flags); +	spin_lock_irq(&mx3_cam->lock);  	list_add_tail(&buf->queue, &mx3_cam->capture);  	if (!mx3_cam->active) @@ -351,7 +350,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)  	if (mx3_cam->active == buf)  		mx3_cam->active = NULL; -	spin_unlock_irqrestore(&mx3_cam->lock, flags); +	spin_unlock_irq(&mx3_cam->lock);  error:  	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);  } @@ -407,7 +406,7 @@ static int mx3_videobuf_init(struct vb2_buffer *vb)  	return 0;  } -static int mx3_stop_streaming(struct vb2_queue *q) +static void mx3_stop_streaming(struct vb2_queue *q)  {  	struct soc_camera_device *icd = soc_camera_from_vb2q(q);  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -431,8 +430,6 @@ static int mx3_stop_streaming(struct vb2_queue *q)  	}  	spin_unlock_irqrestore(&mx3_cam->lock, flags); - -	return 0;  }  static struct vb2_ops mx3_videobuf_ops = { @@ -454,7 +451,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q,  	q->ops = &mx3_videobuf_ops;  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct mx3_camera_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	return vb2_queue_init(q);  } diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 6769193c7c7..74ce8b6b79f 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1495,7 +1495,7 @@ static int omap1_cam_set_bus_param(struct soc_camera_device *icd)  	if (ctrlclock & LCLK_EN)  		CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock); -	/* select bus endianess */ +	/* select bus endianness */  	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);  	fmt = xlate->host_fmt; diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index d02a7e0b773..e594230e84d 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -16,6 +16,7 @@  #include <linux/delay.h>  #include <linux/interrupt.h> +#include <linux/io.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/platform_data/camera-rcar.h> @@ -67,6 +68,8 @@  #define VNMC_YCAL		(1 << 19)  #define VNMC_INF_YUV8_BT656	(0 << 16)  #define VNMC_INF_YUV8_BT601	(1 << 16) +#define VNMC_INF_YUV10_BT656	(2 << 16) +#define VNMC_INF_YUV10_BT601	(3 << 16)  #define VNMC_INF_YUV16		(5 << 16)  #define VNMC_VUP		(1 << 10)  #define VNMC_IM_ODD		(0 << 3) @@ -105,6 +108,7 @@  #define VIN_MAX_HEIGHT		2048  enum chip_id { +	RCAR_GEN2,  	RCAR_H1,  	RCAR_M1,  	RCAR_E1, @@ -273,6 +277,12 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)  		/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */  		vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?  			VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; +		break; +	case V4L2_MBUS_FMT_YUYV10_2X10: +		/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ +		vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ? +			VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; +		break;  	default:  		break;  	} @@ -300,7 +310,8 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)  		dmr = 0;  		break;  	case V4L2_PIX_FMT_RGB32: -		if (priv->chip == RCAR_H1 || priv->chip == RCAR_E1) { +		if (priv->chip == RCAR_GEN2 || priv->chip == RCAR_H1 || +		    priv->chip == RCAR_E1) {  			dmr = VNDMR_EXRGB;  			break;  		} @@ -502,7 +513,7 @@ static int rcar_vin_videobuf_init(struct vb2_buffer *vb)  	return 0;  } -static int rcar_vin_stop_streaming(struct vb2_queue *vq) +static void rcar_vin_stop_streaming(struct vb2_queue *vq)  {  	struct soc_camera_device *icd = soc_camera_from_vb2q(vq);  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -513,8 +524,6 @@ static int rcar_vin_stop_streaming(struct vb2_queue *vq)  	list_for_each_safe(buf_head, tmp, &priv->capture)  		list_del_init(buf_head);  	spin_unlock_irq(&priv->lock); - -	return 0;  }  static struct vb2_ops rcar_vin_vb2_ops = { @@ -1000,6 +1009,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,  	switch (code) {  	case V4L2_MBUS_FMT_YUYV8_1X16:  	case V4L2_MBUS_FMT_YUYV8_2X8: +	case V4L2_MBUS_FMT_YUYV10_2X10:  		if (cam->extra_fmt)  			break; @@ -1357,7 +1367,7 @@ static int rcar_vin_init_videobuf2(struct vb2_queue *vq,  	vq->ops = &rcar_vin_vb2_ops;  	vq->mem_ops = &vb2_dma_contig_memops;  	vq->buf_struct_size = sizeof(struct rcar_vin_buffer); -	vq->timestamp_type  = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	vq->timestamp_flags  = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	return vb2_queue_init(vq);  } @@ -1381,6 +1391,8 @@ static struct soc_camera_host_ops rcar_vin_host_ops = {  };  static struct platform_device_id rcar_vin_id_table[] = { +	{ "r8a7791-vin",  RCAR_GEN2 }, +	{ "r8a7790-vin",  RCAR_GEN2 },  	{ "r8a7779-vin",  RCAR_H1 },  	{ "r8a7778-vin",  RCAR_M1 },  	{ "uPD35004-vin", RCAR_E1 }, diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 8df22f77917..20ad4a571d3 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -471,7 +471,7 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)  	return 0;  } -static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) +static void sh_mobile_ceu_stop_streaming(struct vb2_queue *q)  {  	struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);  	struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -487,7 +487,7 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)  	spin_unlock_irq(&pcdev->lock); -	return sh_mobile_ceu_soft_reset(pcdev); +	sh_mobile_ceu_soft_reset(pcdev);  }  static struct vb2_ops sh_mobile_ceu_videobuf_ops = { @@ -1665,7 +1665,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,  	q->ops = &sh_mobile_ceu_videobuf_ops;  	q->mem_ops = &vb2_dma_contig_memops;  	q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	return vb2_queue_init(q);  } @@ -1800,7 +1800,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)  	/* request irq */  	err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, -			       IRQF_DISABLED, dev_name(&pdev->dev), pcdev); +			       0, dev_name(&pdev->dev), pcdev);  	if (err) {  		dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");  		goto exit_release_mem; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 387a232d95a..7fec8cdaf09 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -71,13 +71,23 @@ static int video_dev_create(struct soc_camera_device *icd);  int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,  			struct v4l2_clk *clk)  { -	int ret = clk ? v4l2_clk_enable(clk) : 0; -	if (ret < 0) { -		dev_err(dev, "Cannot enable clock: %d\n", ret); -		return ret; +	int ret; +	bool clock_toggle; + +	if (clk && (!ssdd->unbalanced_power || +		    !test_and_set_bit(0, &ssdd->clock_state))) { +		ret = v4l2_clk_enable(clk); +		if (ret < 0) { +			dev_err(dev, "Cannot enable clock: %d\n", ret); +			return ret; +		} +		clock_toggle = true; +	} else { +		clock_toggle = false;  	} -	ret = regulator_bulk_enable(ssdd->num_regulators, -					ssdd->regulators); + +	ret = regulator_bulk_enable(ssdd->sd_pdata.num_regulators, +				    ssdd->sd_pdata.regulators);  	if (ret < 0) {  		dev_err(dev, "Cannot enable regulators\n");  		goto eregenable; @@ -95,10 +105,10 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd,  	return 0;  epwron: -	regulator_bulk_disable(ssdd->num_regulators, -			       ssdd->regulators); +	regulator_bulk_disable(ssdd->sd_pdata.num_regulators, +			       ssdd->sd_pdata.regulators);  eregenable: -	if (clk) +	if (clock_toggle)  		v4l2_clk_disable(clk);  	return ret; @@ -120,14 +130,14 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd  		}  	} -	err = regulator_bulk_disable(ssdd->num_regulators, -				     ssdd->regulators); +	err = regulator_bulk_disable(ssdd->sd_pdata.num_regulators, +				     ssdd->sd_pdata.regulators);  	if (err < 0) {  		dev_err(dev, "Cannot disable regulators\n");  		ret = ret ? : err;  	} -	if (clk) +	if (clk && (!ssdd->unbalanced_power || test_and_clear_bit(0, &ssdd->clock_state)))  		v4l2_clk_disable(clk);  	return ret; @@ -137,8 +147,8 @@ EXPORT_SYMBOL(soc_camera_power_off);  int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)  {  	/* Should not have any effect in synchronous case */ -	return devm_regulator_bulk_get(dev, ssdd->num_regulators, -				       ssdd->regulators); +	return devm_regulator_bulk_get(dev, ssdd->sd_pdata.num_regulators, +				       ssdd->sd_pdata.regulators);  }  EXPORT_SYMBOL(soc_camera_power_init); @@ -304,7 +314,7 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id a)  	struct soc_camera_device *icd = file->private_data;  	struct v4l2_subdev *sd = soc_camera_to_subdev(icd); -	return v4l2_subdev_call(sd, core, s_std, a); +	return v4l2_subdev_call(sd, video, s_std, a);  }  static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) @@ -312,7 +322,7 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)  	struct soc_camera_device *icd = file->private_data;  	struct v4l2_subdev *sd = soc_camera_to_subdev(icd); -	return v4l2_subdev_call(sd, core, g_std, a); +	return v4l2_subdev_call(sd, video, g_std, a);  }  static int soc_camera_enum_framesizes(struct file *file, void *fh, @@ -1267,6 +1277,8 @@ static int soc_camera_probe_finish(struct soc_camera_device *icd)  	sd->grp_id = soc_camera_grp_id(icd);  	v4l2_set_subdev_hostdata(sd, icd); +	v4l2_subdev_call(sd, video, g_tvnorms, &icd->vdev->tvnorms); +  	ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);  	if (ret < 0)  		return ret; @@ -1346,8 +1358,8 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,  	 * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try  	 * to allocate them again.  	 */ -	ssdd->num_regulators = 0; -	ssdd->regulators = NULL; +	ssdd->sd_pdata.num_regulators = 0; +	ssdd->sd_pdata.regulators = NULL;  	shd->board_info->platform_data = ssdd;  	snprintf(clk_name, sizeof(clk_name), "%d-%04x", @@ -1987,6 +1999,12 @@ static int soc_camera_video_start(struct soc_camera_device *icd)  		return -ENODEV;  	video_set_drvdata(icd->vdev, icd); +	if (icd->vdev->tvnorms == 0) { +		/* disable the STD API if there are no tvnorms defined */ +		v4l2_disable_ioctl(icd->vdev, VIDIOC_G_STD); +		v4l2_disable_ioctl(icd->vdev, VIDIOC_S_STD); +		v4l2_disable_ioctl(icd->vdev, VIDIOC_ENUMSTD); +	}  	ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);  	if (ret < 0) {  		dev_err(icd->pdev, "video_register_device failed: %d\n", ret); @@ -2020,8 +2038,8 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)  	 * that case regulators are attached to the I2C device and not to the  	 * camera platform device.  	 */ -	ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, -				      ssdd->regulators); +	ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators, +				      ssdd->sd_pdata.regulators);  	if (ret < 0)  		return ret; diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c index cbd3a34f4f3..8e74fb7f2a0 100644 --- a/drivers/media/platform/soc_camera/soc_scale_crop.c +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -141,8 +141,8 @@ int soc_camera_client_s_crop(struct v4l2_subdev *sd,  	 * Popular special case - some cameras can only handle fixed sizes like  	 * QVGA, VGA,... Take care to avoid infinite loop.  	 */ -	width = max(cam_rect->width, 2); -	height = max(cam_rect->height, 2); +	width = max_t(unsigned int, cam_rect->width, 2); +	height = max_t(unsigned int, cam_rect->height, 2);  	/*  	 * Loop as long as sensor is not covering the requested rectangle and diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti-vpe/Makefile new file mode 100644 index 00000000000..be680f839e7 --- /dev/null +++ b/drivers/media/platform/ti-vpe/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_VIDEO_TI_VPE) += ti-vpe.o + +ti-vpe-y := vpe.o sc.o csc.o vpdma.o + +ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c new file mode 100644 index 00000000000..940df4000c4 --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.c @@ -0,0 +1,196 @@ +/* + * Color space converter library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include "csc.h" + +/* + * 16 coefficients in the order: + * a0, b0, c0, a1, b1, c1, a2, b2, c2, d0, d1, d2 + * (we may need to pass non-default values from user space later on, we might + * need to make the coefficient struct more easy to populate) + */ +struct colorspace_coeffs { +	u16	sd[12]; +	u16	hd[12]; +}; + +/* VIDEO_RANGE: limited range, GRAPHICS_RANGE: full range */ +#define	CSC_COEFFS_VIDEO_RANGE_Y2R	0 +#define	CSC_COEFFS_GRAPHICS_RANGE_Y2R	1 +#define	CSC_COEFFS_VIDEO_RANGE_R2Y	2 +#define	CSC_COEFFS_GRAPHICS_RANGE_R2Y	3 + +/* default colorspace coefficients */ +static struct colorspace_coeffs colorspace_coeffs[4] = { +	[CSC_COEFFS_VIDEO_RANGE_Y2R] = { +		{ +			/* SDTV */ +			0x0400, 0x0000, 0x057D, 0x0400, 0x1EA7, 0x1D35, +			0x0400, 0x06EF, 0x1FFE, 0x0D40, 0x0210, 0x0C88, +		}, +		{ +			/* HDTV */ +			0x0400, 0x0000, 0x0629, 0x0400, 0x1F45, 0x1E2B, +			0x0400, 0x0742, 0x0000, 0x0CEC, 0x0148, 0x0C60, +		}, +	}, +	[CSC_COEFFS_GRAPHICS_RANGE_Y2R] = { +		{ +			/* SDTV */ +			0x04A8, 0x1FFE, 0x0662, 0x04A8, 0x1E6F, 0x1CBF, +			0x04A8, 0x0812, 0x1FFF, 0x0C84, 0x0220, 0x0BAC, +		}, +		{ +			/* HDTV */ +			0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, +			0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, +		}, +	}, +	[CSC_COEFFS_VIDEO_RANGE_R2Y] = { +		{ +			/* SDTV */ +			0x0132, 0x0259, 0x0075, 0x1F50, 0x1EA5, 0x020B, +			0x020B, 0x1E4A, 0x1FAB, 0x0000, 0x0200, 0x0200, +		}, +		{ +			/* HDTV */ +			0x00DA, 0x02DC, 0x004A, 0x1F88, 0x1E6C, 0x020C, +			0x020C, 0x1E24, 0x1FD0, 0x0000, 0x0200, 0x0200, +		}, +	}, +	[CSC_COEFFS_GRAPHICS_RANGE_R2Y] = { +		{ +			/* SDTV */ +			0x0107, 0x0204, 0x0064, 0x1F68, 0x1ED6, 0x01C2, +			0x01C2, 0x1E87, 0x1FB7, 0x0040, 0x0200, 0x0200, +		}, +		{ +			/* HDTV */ +			0x04A8, 0x0000, 0x072C, 0x04A8, 0x1F26, 0x1DDE, +			0x04A8, 0x0873, 0x0000, 0x0C20, 0x0134, 0x0B7C, +		}, +	}, +}; + +void csc_dump_regs(struct csc_data *csc) +{ +	struct device *dev = &csc->pdev->dev; + +	u32 read_reg(struct csc_data *csc, int offset) +	{ +		return ioread32(csc->base + offset); +	} + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(csc, CSC_##r)) + +	DUMPREG(CSC00); +	DUMPREG(CSC01); +	DUMPREG(CSC02); +	DUMPREG(CSC03); +	DUMPREG(CSC04); +	DUMPREG(CSC05); + +#undef DUMPREG +} + +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5) +{ +	*csc_reg5 |= CSC_BYPASS; +} + +/* + * set the color space converter coefficient shadow register values + */ +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, +		enum v4l2_colorspace src_colorspace, +		enum v4l2_colorspace dst_colorspace) +{ +	u32 *csc_reg5 = csc_reg0 + 5; +	u32 *shadow_csc = csc_reg0; +	struct colorspace_coeffs *sd_hd_coeffs; +	u16 *coeff, *end_coeff; +	enum v4l2_colorspace yuv_colorspace; +	int sel = 0; + +	/* +	 * support only graphics data range(full range) for now, a control ioctl +	 * would be nice here +	 */ +	/* Y2R */ +	if (dst_colorspace == V4L2_COLORSPACE_SRGB && +			(src_colorspace == V4L2_COLORSPACE_SMPTE170M || +			src_colorspace == V4L2_COLORSPACE_REC709)) { +		/* Y2R */ +		sel = 1; +		yuv_colorspace = src_colorspace; +	} else if ((dst_colorspace == V4L2_COLORSPACE_SMPTE170M || +			dst_colorspace == V4L2_COLORSPACE_REC709) && +			src_colorspace == V4L2_COLORSPACE_SRGB) { +		/* R2Y */ +		sel = 3; +		yuv_colorspace = dst_colorspace; +	} else { +		*csc_reg5 |= CSC_BYPASS; +		return; +	} + +	sd_hd_coeffs = &colorspace_coeffs[sel]; + +	/* select between SD or HD coefficients */ +	if (yuv_colorspace == V4L2_COLORSPACE_SMPTE170M) +		coeff = sd_hd_coeffs->sd; +	else +		coeff = sd_hd_coeffs->hd; + +	end_coeff = coeff + 12; + +	for (; coeff < end_coeff; coeff += 2) +		*shadow_csc++ = (*(coeff + 1) << 16) | *coeff; +} + +struct csc_data *csc_create(struct platform_device *pdev) +{ +	struct csc_data *csc; + +	dev_dbg(&pdev->dev, "csc_create\n"); + +	csc = devm_kzalloc(&pdev->dev, sizeof(*csc), GFP_KERNEL); +	if (!csc) { +		dev_err(&pdev->dev, "couldn't alloc csc_data\n"); +		return ERR_PTR(-ENOMEM); +	} + +	csc->pdev = pdev; + +	csc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +			"csc"); +	if (csc->res == NULL) { +		dev_err(&pdev->dev, "missing platform resources data\n"); +		return ERR_PTR(-ENODEV); +	} + +	csc->base = devm_ioremap_resource(&pdev->dev, csc->res); +	if (IS_ERR(csc->base)) { +		dev_err(&pdev->dev, "failed to ioremap\n"); +		return csc->base; +	} + +	return csc; +} diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti-vpe/csc.h new file mode 100644 index 00000000000..1ad2b6dad56 --- /dev/null +++ b/drivers/media/platform/ti-vpe/csc.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ +#ifndef TI_CSC_H +#define TI_CSC_H + +/* VPE color space converter regs */ +#define CSC_CSC00		0x00 +#define CSC_A0_MASK		0x1fff +#define CSC_A0_SHIFT		0 +#define CSC_B0_MASK		0x1fff +#define CSC_B0_SHIFT		16 + +#define CSC_CSC01		0x04 +#define CSC_C0_MASK		0x1fff +#define CSC_C0_SHIFT		0 +#define CSC_A1_MASK		0x1fff +#define CSC_A1_SHIFT		16 + +#define CSC_CSC02		0x08 +#define CSC_B1_MASK		0x1fff +#define CSC_B1_SHIFT		0 +#define CSC_C1_MASK		0x1fff +#define CSC_C1_SHIFT		16 + +#define CSC_CSC03		0x0c +#define CSC_A2_MASK		0x1fff +#define CSC_A2_SHIFT		0 +#define CSC_B2_MASK		0x1fff +#define CSC_B2_SHIFT		16 + +#define CSC_CSC04		0x10 +#define CSC_C2_MASK		0x1fff +#define CSC_C2_SHIFT		0 +#define CSC_D0_MASK		0x0fff +#define CSC_D0_SHIFT		16 + +#define CSC_CSC05		0x14 +#define CSC_D1_MASK		0x0fff +#define CSC_D1_SHIFT		0 +#define CSC_D2_MASK		0x0fff +#define CSC_D2_SHIFT		16 + +#define CSC_BYPASS		(1 << 28) + +struct csc_data { +	void __iomem		*base; +	struct resource		*res; + +	struct platform_device	*pdev; +}; + +void csc_dump_regs(struct csc_data *csc); +void csc_set_coeff_bypass(struct csc_data *csc, u32 *csc_reg5); +void csc_set_coeff(struct csc_data *csc, u32 *csc_reg0, +		enum v4l2_colorspace src_colorspace, +		enum v4l2_colorspace dst_colorspace); +struct csc_data *csc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c new file mode 100644 index 00000000000..6314171ffe9 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.c @@ -0,0 +1,311 @@ +/* + * Scaler library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "sc.h" +#include "sc_coeff.h" + +void sc_dump_regs(struct sc_data *sc) +{ +	struct device *dev = &sc->pdev->dev; + +	u32 read_reg(struct sc_data *sc, int offset) +	{ +		return ioread32(sc->base + offset); +	} + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(sc, CFG_##r)) + +	DUMPREG(SC0); +	DUMPREG(SC1); +	DUMPREG(SC2); +	DUMPREG(SC3); +	DUMPREG(SC4); +	DUMPREG(SC5); +	DUMPREG(SC6); +	DUMPREG(SC8); +	DUMPREG(SC9); +	DUMPREG(SC10); +	DUMPREG(SC11); +	DUMPREG(SC12); +	DUMPREG(SC13); +	DUMPREG(SC17); +	DUMPREG(SC18); +	DUMPREG(SC19); +	DUMPREG(SC20); +	DUMPREG(SC21); +	DUMPREG(SC22); +	DUMPREG(SC23); +	DUMPREG(SC24); +	DUMPREG(SC25); + +#undef DUMPREG +} + +/* + * set the horizontal scaler coefficients according to the ratio of output to + * input widths, after accounting for up to two levels of decimation + */ +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, +		unsigned int dst_w) +{ +	int sixteenths; +	int idx; +	int i, j; +	u16 *coeff_h = addr; +	const u16 *cp; + +	if (dst_w > src_w) { +		idx = HS_UP_SCALE; +	} else { +		if ((dst_w << 1) < src_w) +			dst_w <<= 1;	/* first level decimation */ +		if ((dst_w << 1) < src_w) +			dst_w <<= 1;	/* second level decimation */ + +		if (dst_w == src_w) { +			idx = HS_LE_16_16_SCALE; +		} else { +			sixteenths = (dst_w << 4) / src_w; +			if (sixteenths < 8) +				sixteenths = 8; +			idx = HS_LT_9_16_SCALE + sixteenths - 8; +		} +	} + +	if (idx == sc->hs_index) +		return; + +	cp = scaler_hs_coeffs[idx]; + +	for (i = 0; i < SC_NUM_PHASES * 2; i++) { +		for (j = 0; j < SC_H_NUM_TAPS; j++) +			*coeff_h++ = *cp++; +		/* +		 * for each phase, the scaler expects space for 8 coefficients +		 * in it's memory. For the horizontal scaler, we copy the first +		 * 7 coefficients and skip the last slot to move to the next +		 * row to hold coefficients for the next phase +		 */ +		coeff_h += SC_NUM_TAPS_MEM_ALIGN - SC_H_NUM_TAPS; +	} + +	sc->hs_index = idx; + +	sc->load_coeff_h = true; +} + +/* + * set the vertical scaler coefficients according to the ratio of output to + * input heights + */ +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, +		unsigned int dst_h) +{ +	int sixteenths; +	int idx; +	int i, j; +	u16 *coeff_v = addr; +	const u16 *cp; + +	if (dst_h > src_h) { +		idx = VS_UP_SCALE; +	} else if (dst_h == src_h) { +		idx = VS_1_TO_1_SCALE; +	} else { +		sixteenths = (dst_h << 4) / src_h; +		if (sixteenths < 8) +			sixteenths = 8; +		idx = VS_LT_9_16_SCALE + sixteenths - 8; +	} + +	if (idx == sc->vs_index) +		return; + +	cp = scaler_vs_coeffs[idx]; + +	for (i = 0; i < SC_NUM_PHASES * 2; i++) { +		for (j = 0; j < SC_V_NUM_TAPS; j++) +			*coeff_v++ = *cp++; +		/* +		 * for the vertical scaler, we copy the first 5 coefficients and +		 * skip the last 3 slots to move to the next row to hold +		 * coefficients for the next phase +		 */ +		coeff_v += SC_NUM_TAPS_MEM_ALIGN - SC_V_NUM_TAPS; +	} + +	sc->vs_index = idx; +	sc->load_coeff_v = true; +} + +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, +		u32 *sc_reg17, unsigned int src_w, unsigned int src_h, +		unsigned int dst_w, unsigned int dst_h) +{ +	struct device *dev = &sc->pdev->dev; +	u32 val; +	int dcm_x, dcm_shift; +	bool use_rav; +	unsigned long lltmp; +	u32 lin_acc_inc, lin_acc_inc_u; +	u32 col_acc_offset; +	u16 factor = 0; +	int row_acc_init_rav = 0, row_acc_init_rav_b = 0; +	u32 row_acc_inc = 0, row_acc_offset = 0, row_acc_offset_b = 0; +	/* +	 * location of SC register in payload memory with respect to the first +	 * register in the mmr address data block +	 */ +	u32 *sc_reg9 = sc_reg8 + 1; +	u32 *sc_reg12 = sc_reg8 + 4; +	u32 *sc_reg13 = sc_reg8 + 5; +	u32 *sc_reg24 = sc_reg17 + 7; + +	val = sc_reg0[0]; + +	/* clear all the features(they may get enabled elsewhere later) */ +	val &= ~(CFG_SELFGEN_FID | CFG_TRIM | CFG_ENABLE_SIN2_VER_INTP | +		CFG_INTERLACE_I | CFG_DCM_4X | CFG_DCM_2X | CFG_AUTO_HS | +		CFG_ENABLE_EV | CFG_USE_RAV | CFG_INVT_FID | CFG_SC_BYPASS | +		CFG_INTERLACE_O | CFG_Y_PK_EN | CFG_HP_BYPASS | CFG_LINEAR); + +	if (src_w == dst_w && src_h == dst_h) { +		val |= CFG_SC_BYPASS; +		sc_reg0[0] = val; +		return; +	} + +	/* we only support linear scaling for now */ +	val |= CFG_LINEAR; + +	/* configure horizontal scaler */ + +	/* enable 2X or 4X decimation */ +	dcm_x = src_w / dst_w; +	if (dcm_x > 4) { +		val |= CFG_DCM_4X; +		dcm_shift = 2; +	} else if (dcm_x > 2) { +		val |= CFG_DCM_2X; +		dcm_shift = 1; +	} else { +		dcm_shift = 0; +	} + +	lltmp = dst_w - 1; +	lin_acc_inc = div64_u64(((u64)(src_w >> dcm_shift) - 1) << 24, lltmp); +	lin_acc_inc_u = 0; +	col_acc_offset = 0; + +	dev_dbg(dev, "hs config: src_w = %d, dst_w = %d, decimation = %s, lin_acc_inc = %08x\n", +		src_w, dst_w, dcm_shift == 2 ? "4x" : +		(dcm_shift == 1 ? "2x" : "none"), lin_acc_inc); + +	/* configure vertical scaler */ + +	/* use RAV for vertical scaler if vertical downscaling is > 4x */ +	if (dst_h < (src_h >> 2)) { +		use_rav = true; +		val |= CFG_USE_RAV; +	} else { +		use_rav = false; +	} + +	if (use_rav) { +		/* use RAV */ +		factor = (u16) ((dst_h << 10) / src_h); + +		row_acc_init_rav = factor + ((1 + factor) >> 1); +		if (row_acc_init_rav >= 1024) +			row_acc_init_rav -= 1024; + +		row_acc_init_rav_b = row_acc_init_rav + +				(1 + (row_acc_init_rav >> 1)) - +				(1024 >> 1); + +		if (row_acc_init_rav_b < 0) { +			row_acc_init_rav_b += row_acc_init_rav; +			row_acc_init_rav *= 2; +		} + +		dev_dbg(dev, "vs config(RAV): src_h = %d, dst_h = %d, factor = %d, acc_init = %08x, acc_init_b = %08x\n", +			src_h, dst_h, factor, row_acc_init_rav, +			row_acc_init_rav_b); +	} else { +		/* use polyphase */ +		row_acc_inc = ((src_h - 1) << 16) / (dst_h - 1); +		row_acc_offset = 0; +		row_acc_offset_b = 0; + +		dev_dbg(dev, "vs config(POLY): src_h = %d, dst_h = %d,row_acc_inc = %08x\n", +			src_h, dst_h, row_acc_inc); +	} + + +	sc_reg0[0] = val; +	sc_reg0[1] = row_acc_inc; +	sc_reg0[2] = row_acc_offset; +	sc_reg0[3] = row_acc_offset_b; + +	sc_reg0[4] = ((lin_acc_inc_u & CFG_LIN_ACC_INC_U_MASK) << +			CFG_LIN_ACC_INC_U_SHIFT) | (dst_w << CFG_TAR_W_SHIFT) | +			(dst_h << CFG_TAR_H_SHIFT); + +	sc_reg0[5] = (src_w << CFG_SRC_W_SHIFT) | (src_h << CFG_SRC_H_SHIFT); + +	sc_reg0[6] = (row_acc_init_rav_b << CFG_ROW_ACC_INIT_RAV_B_SHIFT) | +		(row_acc_init_rav << CFG_ROW_ACC_INIT_RAV_SHIFT); + +	*sc_reg9 = lin_acc_inc; + +	*sc_reg12 = col_acc_offset << CFG_COL_ACC_OFFSET_SHIFT; + +	*sc_reg13 = factor; + +	*sc_reg24 = (src_w << CFG_ORG_W_SHIFT) | (src_h << CFG_ORG_H_SHIFT); +} + +struct sc_data *sc_create(struct platform_device *pdev) +{ +	struct sc_data *sc; + +	dev_dbg(&pdev->dev, "sc_create\n"); + +	sc = devm_kzalloc(&pdev->dev, sizeof(*sc), GFP_KERNEL); +	if (!sc) { +		dev_err(&pdev->dev, "couldn't alloc sc_data\n"); +		return ERR_PTR(-ENOMEM); +	} + +	sc->pdev = pdev; + +	sc->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sc"); +	if (!sc->res) { +		dev_err(&pdev->dev, "missing platform resources data\n"); +		return ERR_PTR(-ENODEV); +	} + +	sc->base = devm_ioremap_resource(&pdev->dev, sc->res); +	if (IS_ERR(sc->base)) { +		dev_err(&pdev->dev, "failed to ioremap\n"); +		return sc->base; +	} + +	return sc; +} diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti-vpe/sc.h new file mode 100644 index 00000000000..60e411e05c3 --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ +#ifndef TI_SC_H +#define TI_SC_H + +/* Scaler regs */ +#define CFG_SC0				0x0 +#define CFG_INTERLACE_O			(1 << 0) +#define CFG_LINEAR			(1 << 1) +#define CFG_SC_BYPASS			(1 << 2) +#define CFG_INVT_FID			(1 << 3) +#define CFG_USE_RAV			(1 << 4) +#define CFG_ENABLE_EV			(1 << 5) +#define CFG_AUTO_HS			(1 << 6) +#define CFG_DCM_2X			(1 << 7) +#define CFG_DCM_4X			(1 << 8) +#define CFG_HP_BYPASS			(1 << 9) +#define CFG_INTERLACE_I			(1 << 10) +#define CFG_ENABLE_SIN2_VER_INTP	(1 << 11) +#define CFG_Y_PK_EN			(1 << 14) +#define CFG_TRIM			(1 << 15) +#define CFG_SELFGEN_FID			(1 << 16) + +#define CFG_SC1				0x4 +#define CFG_ROW_ACC_INC_MASK		0x07ffffff +#define CFG_ROW_ACC_INC_SHIFT		0 + +#define CFG_SC2				0x08 +#define CFG_ROW_ACC_OFFSET_MASK		0x0fffffff +#define CFG_ROW_ACC_OFFSET_SHIFT	0 + +#define CFG_SC3				0x0c +#define CFG_ROW_ACC_OFFSET_B_MASK	0x0fffffff +#define CFG_ROW_ACC_OFFSET_B_SHIFT	0 + +#define CFG_SC4				0x10 +#define CFG_TAR_H_MASK			0x07ff +#define CFG_TAR_H_SHIFT			0 +#define CFG_TAR_W_MASK			0x07ff +#define CFG_TAR_W_SHIFT			12 +#define CFG_LIN_ACC_INC_U_MASK		0x07 +#define CFG_LIN_ACC_INC_U_SHIFT		24 +#define CFG_NLIN_ACC_INIT_U_MASK	0x07 +#define CFG_NLIN_ACC_INIT_U_SHIFT	28 + +#define CFG_SC5				0x14 +#define CFG_SRC_H_MASK			0x07ff +#define CFG_SRC_H_SHIFT			0 +#define CFG_SRC_W_MASK			0x07ff +#define CFG_SRC_W_SHIFT			12 +#define CFG_NLIN_ACC_INC_U_MASK		0x07 +#define CFG_NLIN_ACC_INC_U_SHIFT	24 + +#define CFG_SC6				0x18 +#define CFG_ROW_ACC_INIT_RAV_MASK	0x03ff +#define CFG_ROW_ACC_INIT_RAV_SHIFT	0 +#define CFG_ROW_ACC_INIT_RAV_B_MASK	0x03ff +#define CFG_ROW_ACC_INIT_RAV_B_SHIFT	10 + +#define CFG_SC8				0x20 +#define CFG_NLIN_LEFT_MASK		0x07ff +#define CFG_NLIN_LEFT_SHIFT		0 +#define CFG_NLIN_RIGHT_MASK		0x07ff +#define CFG_NLIN_RIGHT_SHIFT		12 + +#define CFG_SC9				0x24 +#define CFG_LIN_ACC_INC			CFG_SC9 + +#define CFG_SC10			0x28 +#define CFG_NLIN_ACC_INIT		CFG_SC10 + +#define CFG_SC11			0x2c +#define CFG_NLIN_ACC_INC		CFG_SC11 + +#define CFG_SC12			0x30 +#define CFG_COL_ACC_OFFSET_MASK		0x01ffffff +#define CFG_COL_ACC_OFFSET_SHIFT	0 + +#define CFG_SC13			0x34 +#define CFG_SC_FACTOR_RAV_MASK		0xff +#define CFG_SC_FACTOR_RAV_SHIFT		0 +#define CFG_CHROMA_INTP_THR_MASK	0x03ff +#define CFG_CHROMA_INTP_THR_SHIFT	12 +#define CFG_DELTA_CHROMA_THR_MASK	0x0f +#define CFG_DELTA_CHROMA_THR_SHIFT	24 + +#define CFG_SC17			0x44 +#define CFG_EV_THR_MASK			0x03ff +#define CFG_EV_THR_SHIFT		12 +#define CFG_DELTA_LUMA_THR_MASK		0x0f +#define CFG_DELTA_LUMA_THR_SHIFT	24 +#define CFG_DELTA_EV_THR_MASK		0x0f +#define CFG_DELTA_EV_THR_SHIFT		28 + +#define CFG_SC18			0x48 +#define CFG_HS_FACTOR_MASK		0x03ff +#define CFG_HS_FACTOR_SHIFT		0 +#define CFG_CONF_DEFAULT_MASK		0x01ff +#define CFG_CONF_DEFAULT_SHIFT		16 + +#define CFG_SC19			0x4c +#define CFG_HPF_COEFF0_MASK		0xff +#define CFG_HPF_COEFF0_SHIFT		0 +#define CFG_HPF_COEFF1_MASK		0xff +#define CFG_HPF_COEFF1_SHIFT		8 +#define CFG_HPF_COEFF2_MASK		0xff +#define CFG_HPF_COEFF2_SHIFT		16 +#define CFG_HPF_COEFF3_MASK		0xff +#define CFG_HPF_COEFF3_SHIFT		23 + +#define CFG_SC20			0x50 +#define CFG_HPF_COEFF4_MASK		0xff +#define CFG_HPF_COEFF4_SHIFT		0 +#define CFG_HPF_COEFF5_MASK		0xff +#define CFG_HPF_COEFF5_SHIFT		8 +#define CFG_HPF_NORM_SHIFT_MASK		0x07 +#define CFG_HPF_NORM_SHIFT_SHIFT	16 +#define CFG_NL_LIMIT_MASK		0x1ff +#define CFG_NL_LIMIT_SHIFT		20 + +#define CFG_SC21			0x54 +#define CFG_NL_LO_THR_MASK		0x01ff +#define CFG_NL_LO_THR_SHIFT		0 +#define CFG_NL_LO_SLOPE_MASK		0xff +#define CFG_NL_LO_SLOPE_SHIFT		16 + +#define CFG_SC22			0x58 +#define CFG_NL_HI_THR_MASK		0x01ff +#define CFG_NL_HI_THR_SHIFT		0 +#define CFG_NL_HI_SLOPE_SH_MASK		0x07 +#define CFG_NL_HI_SLOPE_SH_SHIFT	16 + +#define CFG_SC23			0x5c +#define CFG_GRADIENT_THR_MASK		0x07ff +#define CFG_GRADIENT_THR_SHIFT		0 +#define CFG_GRADIENT_THR_RANGE_MASK	0x0f +#define CFG_GRADIENT_THR_RANGE_SHIFT	12 +#define CFG_MIN_GY_THR_MASK		0xff +#define CFG_MIN_GY_THR_SHIFT		16 +#define CFG_MIN_GY_THR_RANGE_MASK	0x0f +#define CFG_MIN_GY_THR_RANGE_SHIFT	28 + +#define CFG_SC24			0x60 +#define CFG_ORG_H_MASK			0x07ff +#define CFG_ORG_H_SHIFT			0 +#define CFG_ORG_W_MASK			0x07ff +#define CFG_ORG_W_SHIFT			16 + +#define CFG_SC25			0x64 +#define CFG_OFF_H_MASK			0x07ff +#define CFG_OFF_H_SHIFT			0 +#define CFG_OFF_W_MASK			0x07ff +#define CFG_OFF_W_SHIFT			16 + +/* number of phases supported by the polyphase scalers */ +#define SC_NUM_PHASES			32 + +/* number of taps used by horizontal polyphase scaler */ +#define SC_H_NUM_TAPS			7 + +/* number of taps used by vertical polyphase scaler */ +#define SC_V_NUM_TAPS			5 + +/* number of taps expected by the scaler in it's coefficient memory */ +#define SC_NUM_TAPS_MEM_ALIGN		8 + +/* + * coefficient memory size in bytes: + * num phases x num sets(luma and chroma) x num taps(aligned) x coeff size + */ +#define SC_COEF_SRAM_SIZE	(SC_NUM_PHASES * 2 * SC_NUM_TAPS_MEM_ALIGN * 2) + +struct sc_data { +	void __iomem		*base; +	struct resource		*res; + +	dma_addr_t		loaded_coeff_h; /* loaded h coeffs in SC */ +	dma_addr_t		loaded_coeff_v; /* loaded v coeffs in SC */ + +	bool			load_coeff_h;	/* have new h SC coeffs */ +	bool			load_coeff_v;	/* have new v SC coeffs */ + +	unsigned int		hs_index;	/* h SC coeffs selector */ +	unsigned int		vs_index;	/* v SC coeffs selector */ + +	struct platform_device *pdev; +}; + +void sc_dump_regs(struct sc_data *sc); +void sc_set_hs_coeffs(struct sc_data *sc, void *addr, unsigned int src_w, +		unsigned int dst_w); +void sc_set_vs_coeffs(struct sc_data *sc, void *addr, unsigned int src_h, +		unsigned int dst_h); +void sc_config_scaler(struct sc_data *sc, u32 *sc_reg0, u32 *sc_reg8, +		u32 *sc_reg17, unsigned int src_w, unsigned int src_h, +		unsigned int dst_w, unsigned int dst_h); +struct sc_data *sc_create(struct platform_device *pdev); + +#endif diff --git a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti-vpe/sc_coeff.h new file mode 100644 index 00000000000..5bfa5c03aec --- /dev/null +++ b/drivers/media/platform/ti-vpe/sc_coeff.h @@ -0,0 +1,1342 @@ +/* + * VPE SC coefs + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#ifndef __TI_SC_COEFF_H +#define __TI_SC_COEFF_H + +/* horizontal scaler coefficients */ +enum { +	HS_UP_SCALE = 0, +	HS_LT_9_16_SCALE, +	HS_LT_10_16_SCALE, +	HS_LT_11_16_SCALE, +	HS_LT_12_16_SCALE, +	HS_LT_13_16_SCALE, +	HS_LT_14_16_SCALE, +	HS_LT_15_16_SCALE, +	HS_LE_16_16_SCALE, +}; + +static const u16 scaler_hs_coeffs[13][SC_NUM_PHASES * 2 * SC_H_NUM_TAPS] = { +	[HS_UP_SCALE] = { +		/* Luma */ +		0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, +		0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, +		0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, +		0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, +		0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, +		0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, +		0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, +		0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, +		0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, +		0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, +		0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, +		0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, +		0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, +		0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, +		0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, +		0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, +		0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, +		0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, +		0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, +		0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, +		0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, +		0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, +		0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, +		0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, +		0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, +		0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, +		0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, +		0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, +		0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, +		0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, +		0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, +		0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, +		/* Chroma */ +		0x001F, 0x1F90, 0x00D2, 0x06FE, 0x00D2, 0x1F90, 0x001F, +		0x001C, 0x1F9E, 0x009F, 0x06FB, 0x0108, 0x1F82, 0x0022, +		0x0019, 0x1FAC, 0x006F, 0x06F3, 0x0140, 0x1F74, 0x0025, +		0x0016, 0x1FB9, 0x0041, 0x06E7, 0x017B, 0x1F66, 0x0028, +		0x0013, 0x1FC6, 0x0017, 0x06D6, 0x01B7, 0x1F58, 0x002B, +		0x0010, 0x1FD3, 0x1FEF, 0x06C0, 0x01F6, 0x1F4B, 0x002D, +		0x000E, 0x1FDF, 0x1FCB, 0x06A5, 0x0235, 0x1F3F, 0x002F, +		0x000B, 0x1FEA, 0x1FAA, 0x0686, 0x0277, 0x1F33, 0x0031, +		0x0009, 0x1FF5, 0x1F8C, 0x0663, 0x02B8, 0x1F28, 0x0033, +		0x0007, 0x1FFF, 0x1F72, 0x063A, 0x02FB, 0x1F1F, 0x0034, +		0x0005, 0x0008, 0x1F5A, 0x060F, 0x033E, 0x1F17, 0x0035, +		0x0003, 0x0010, 0x1F46, 0x05E0, 0x0382, 0x1F10, 0x0035, +		0x0002, 0x0017, 0x1F34, 0x05AF, 0x03C5, 0x1F0B, 0x0034, +		0x0001, 0x001E, 0x1F26, 0x0579, 0x0407, 0x1F08, 0x0033, +		0x0000, 0x0023, 0x1F1A, 0x0541, 0x0449, 0x1F07, 0x0032, +		0x1FFF, 0x0028, 0x1F12, 0x0506, 0x048A, 0x1F08, 0x002F, +		0x002C, 0x1F0C, 0x04C8, 0x04C8, 0x1F0C, 0x002C, 0x0000, +		0x002F, 0x1F08, 0x048A, 0x0506, 0x1F12, 0x0028, 0x1FFF, +		0x0032, 0x1F07, 0x0449, 0x0541, 0x1F1A, 0x0023, 0x0000, +		0x0033, 0x1F08, 0x0407, 0x0579, 0x1F26, 0x001E, 0x0001, +		0x0034, 0x1F0B, 0x03C5, 0x05AF, 0x1F34, 0x0017, 0x0002, +		0x0035, 0x1F10, 0x0382, 0x05E0, 0x1F46, 0x0010, 0x0003, +		0x0035, 0x1F17, 0x033E, 0x060F, 0x1F5A, 0x0008, 0x0005, +		0x0034, 0x1F1F, 0x02FB, 0x063A, 0x1F72, 0x1FFF, 0x0007, +		0x0033, 0x1F28, 0x02B8, 0x0663, 0x1F8C, 0x1FF5, 0x0009, +		0x0031, 0x1F33, 0x0277, 0x0686, 0x1FAA, 0x1FEA, 0x000B, +		0x002F, 0x1F3F, 0x0235, 0x06A5, 0x1FCB, 0x1FDF, 0x000E, +		0x002D, 0x1F4B, 0x01F6, 0x06C0, 0x1FEF, 0x1FD3, 0x0010, +		0x002B, 0x1F58, 0x01B7, 0x06D6, 0x0017, 0x1FC6, 0x0013, +		0x0028, 0x1F66, 0x017B, 0x06E7, 0x0041, 0x1FB9, 0x0016, +		0x0025, 0x1F74, 0x0140, 0x06F3, 0x006F, 0x1FAC, 0x0019, +		0x0022, 0x1F82, 0x0108, 0x06FB, 0x009F, 0x1F9E, 0x001C, +	}, +	[HS_LT_9_16_SCALE] = { +		/* Luma */ +		0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, +		0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, +		0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, +		0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, +		0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, +		0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, +		0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, +		0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, +		0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, +		0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, +		0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, +		0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, +		0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, +		0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, +		0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, +		0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, +		0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, +		0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, +		0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, +		0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, +		0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, +		0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, +		0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, +		0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, +		0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, +		0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, +		0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, +		0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, +		0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, +		0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, +		0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, +		0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, +		/* Chroma */ +		0x1FA3, 0x005E, 0x024A, 0x036A, 0x024A, 0x005E, 0x1FA3, +		0x1FA3, 0x0052, 0x023A, 0x036A, 0x0259, 0x006A, 0x1FA4, +		0x1FA3, 0x0046, 0x022A, 0x036A, 0x0269, 0x0076, 0x1FA4, +		0x1FA3, 0x003B, 0x021A, 0x0368, 0x0278, 0x0083, 0x1FA5, +		0x1FA4, 0x0031, 0x020A, 0x0365, 0x0286, 0x0090, 0x1FA6, +		0x1FA5, 0x0026, 0x01F9, 0x0362, 0x0294, 0x009E, 0x1FA8, +		0x1FA6, 0x001C, 0x01E8, 0x035E, 0x02A3, 0x00AB, 0x1FAA, +		0x1FA7, 0x0013, 0x01D7, 0x035A, 0x02B0, 0x00B9, 0x1FAC, +		0x1FA9, 0x000A, 0x01C6, 0x0354, 0x02BD, 0x00C7, 0x1FAF, +		0x1FAA, 0x0001, 0x01B6, 0x034E, 0x02C9, 0x00D6, 0x1FB2, +		0x1FAC, 0x1FF9, 0x01A5, 0x0347, 0x02D5, 0x00E5, 0x1FB5, +		0x1FAE, 0x1FF1, 0x0194, 0x0340, 0x02E1, 0x00F3, 0x1FB9, +		0x1FB0, 0x1FEA, 0x0183, 0x0338, 0x02EC, 0x0102, 0x1FBD, +		0x1FB2, 0x1FE3, 0x0172, 0x0330, 0x02F6, 0x0112, 0x1FC1, +		0x1FB4, 0x1FDC, 0x0161, 0x0327, 0x0301, 0x0121, 0x1FC6, +		0x1FB7, 0x1FD6, 0x0151, 0x031D, 0x030A, 0x0130, 0x1FCB, +		0x1FD2, 0x0136, 0x02F8, 0x02F8, 0x0136, 0x1FD2, 0x0000, +		0x1FCB, 0x0130, 0x030A, 0x031D, 0x0151, 0x1FD6, 0x1FB7, +		0x1FC6, 0x0121, 0x0301, 0x0327, 0x0161, 0x1FDC, 0x1FB4, +		0x1FC1, 0x0112, 0x02F6, 0x0330, 0x0172, 0x1FE3, 0x1FB2, +		0x1FBD, 0x0102, 0x02EC, 0x0338, 0x0183, 0x1FEA, 0x1FB0, +		0x1FB9, 0x00F3, 0x02E1, 0x0340, 0x0194, 0x1FF1, 0x1FAE, +		0x1FB5, 0x00E5, 0x02D5, 0x0347, 0x01A5, 0x1FF9, 0x1FAC, +		0x1FB2, 0x00D6, 0x02C9, 0x034E, 0x01B6, 0x0001, 0x1FAA, +		0x1FAF, 0x00C7, 0x02BD, 0x0354, 0x01C6, 0x000A, 0x1FA9, +		0x1FAC, 0x00B9, 0x02B0, 0x035A, 0x01D7, 0x0013, 0x1FA7, +		0x1FAA, 0x00AB, 0x02A3, 0x035E, 0x01E8, 0x001C, 0x1FA6, +		0x1FA8, 0x009E, 0x0294, 0x0362, 0x01F9, 0x0026, 0x1FA5, +		0x1FA6, 0x0090, 0x0286, 0x0365, 0x020A, 0x0031, 0x1FA4, +		0x1FA5, 0x0083, 0x0278, 0x0368, 0x021A, 0x003B, 0x1FA3, +		0x1FA4, 0x0076, 0x0269, 0x036A, 0x022A, 0x0046, 0x1FA3, +		0x1FA4, 0x006A, 0x0259, 0x036A, 0x023A, 0x0052, 0x1FA3, +	}, +	[HS_LT_10_16_SCALE] = { +		/* Luma */ +		0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, +		0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, +		0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, +		0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, +		0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, +		0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, +		0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, +		0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, +		0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, +		0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, +		0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, +		0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, +		0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, +		0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, +		0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, +		0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, +		0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, +		0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, +		0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, +		0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, +		0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, +		0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, +		0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, +		0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, +		0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, +		0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, +		0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, +		0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, +		0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, +		0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, +		0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, +		0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, +		/* Chroma */ +		0x1F8D, 0x000C, 0x026A, 0x03FA, 0x026A, 0x000C, 0x1F8D, +		0x1F8F, 0x0000, 0x0255, 0x03FA, 0x027F, 0x0019, 0x1F8A, +		0x1F92, 0x1FF5, 0x023F, 0x03F8, 0x0293, 0x0027, 0x1F88, +		0x1F95, 0x1FEA, 0x022A, 0x03F6, 0x02A7, 0x0034, 0x1F86, +		0x1F99, 0x1FDF, 0x0213, 0x03F2, 0x02BB, 0x0043, 0x1F85, +		0x1F9C, 0x1FD5, 0x01FE, 0x03ED, 0x02CF, 0x0052, 0x1F83, +		0x1FA0, 0x1FCC, 0x01E8, 0x03E7, 0x02E1, 0x0061, 0x1F83, +		0x1FA4, 0x1FC3, 0x01D2, 0x03E0, 0x02F4, 0x0071, 0x1F82, +		0x1FA7, 0x1FBB, 0x01BC, 0x03D9, 0x0306, 0x0081, 0x1F82, +		0x1FAB, 0x1FB4, 0x01A6, 0x03D0, 0x0317, 0x0092, 0x1F82, +		0x1FAF, 0x1FAD, 0x0190, 0x03C7, 0x0327, 0x00A3, 0x1F83, +		0x1FB3, 0x1FA7, 0x017A, 0x03BC, 0x0337, 0x00B5, 0x1F84, +		0x1FB8, 0x1FA1, 0x0165, 0x03B0, 0x0346, 0x00C7, 0x1F85, +		0x1FBC, 0x1F9C, 0x0150, 0x03A4, 0x0354, 0x00D9, 0x1F87, +		0x1FC0, 0x1F98, 0x013A, 0x0397, 0x0361, 0x00EC, 0x1F8A, +		0x1FC4, 0x1F93, 0x0126, 0x0389, 0x036F, 0x00FE, 0x1F8D, +		0x1F93, 0x010A, 0x0363, 0x0363, 0x010A, 0x1F93, 0x0000, +		0x1F8D, 0x00FE, 0x036F, 0x0389, 0x0126, 0x1F93, 0x1FC4, +		0x1F8A, 0x00EC, 0x0361, 0x0397, 0x013A, 0x1F98, 0x1FC0, +		0x1F87, 0x00D9, 0x0354, 0x03A4, 0x0150, 0x1F9C, 0x1FBC, +		0x1F85, 0x00C7, 0x0346, 0x03B0, 0x0165, 0x1FA1, 0x1FB8, +		0x1F84, 0x00B5, 0x0337, 0x03BC, 0x017A, 0x1FA7, 0x1FB3, +		0x1F83, 0x00A3, 0x0327, 0x03C7, 0x0190, 0x1FAD, 0x1FAF, +		0x1F82, 0x0092, 0x0317, 0x03D0, 0x01A6, 0x1FB4, 0x1FAB, +		0x1F82, 0x0081, 0x0306, 0x03D9, 0x01BC, 0x1FBB, 0x1FA7, +		0x1F82, 0x0071, 0x02F4, 0x03E0, 0x01D2, 0x1FC3, 0x1FA4, +		0x1F83, 0x0061, 0x02E1, 0x03E7, 0x01E8, 0x1FCC, 0x1FA0, +		0x1F83, 0x0052, 0x02CF, 0x03ED, 0x01FE, 0x1FD5, 0x1F9C, +		0x1F85, 0x0043, 0x02BB, 0x03F2, 0x0213, 0x1FDF, 0x1F99, +		0x1F86, 0x0034, 0x02A7, 0x03F6, 0x022A, 0x1FEA, 0x1F95, +		0x1F88, 0x0027, 0x0293, 0x03F8, 0x023F, 0x1FF5, 0x1F92, +		0x1F8A, 0x0019, 0x027F, 0x03FA, 0x0255, 0x0000, 0x1F8F, +	}, +	[HS_LT_11_16_SCALE] = { +		/* Luma */ +		0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, +		0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, +		0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, +		0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, +		0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, +		0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, +		0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, +		0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, +		0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, +		0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, +		0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, +		0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, +		0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, +		0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, +		0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, +		0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, +		0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, +		0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, +		0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, +		0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, +		0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, +		0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, +		0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, +		0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, +		0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, +		0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, +		0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, +		0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, +		0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, +		0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, +		0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, +		0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, +		/* Chroma */ +		0x1F95, 0x1FB5, 0x0272, 0x0488, 0x0272, 0x1FB5, 0x1F95, +		0x1F9B, 0x1FAA, 0x0257, 0x0486, 0x028D, 0x1FC1, 0x1F90, +		0x1FA0, 0x1FA0, 0x023C, 0x0485, 0x02A8, 0x1FCD, 0x1F8A, +		0x1FA6, 0x1F96, 0x0221, 0x0481, 0x02C2, 0x1FDB, 0x1F85, +		0x1FAC, 0x1F8E, 0x0205, 0x047C, 0x02DC, 0x1FE9, 0x1F80, +		0x1FB1, 0x1F86, 0x01E9, 0x0476, 0x02F6, 0x1FF8, 0x1F7C, +		0x1FB7, 0x1F7F, 0x01CE, 0x046E, 0x030F, 0x0008, 0x1F77, +		0x1FBD, 0x1F79, 0x01B3, 0x0465, 0x0326, 0x0019, 0x1F73, +		0x1FC3, 0x1F73, 0x0197, 0x045B, 0x033E, 0x002A, 0x1F70, +		0x1FC8, 0x1F6F, 0x017D, 0x044E, 0x0355, 0x003C, 0x1F6D, +		0x1FCE, 0x1F6B, 0x0162, 0x0441, 0x036B, 0x004F, 0x1F6A, +		0x1FD3, 0x1F68, 0x0148, 0x0433, 0x0380, 0x0063, 0x1F67, +		0x1FD8, 0x1F65, 0x012E, 0x0424, 0x0395, 0x0077, 0x1F65, +		0x1FDE, 0x1F63, 0x0115, 0x0413, 0x03A8, 0x008B, 0x1F64, +		0x1FE3, 0x1F62, 0x00FC, 0x0403, 0x03BA, 0x00A0, 0x1F62, +		0x1FE7, 0x1F62, 0x00E4, 0x03EF, 0x03CC, 0x00B6, 0x1F62, +		0x1F63, 0x00CA, 0x03D3, 0x03D3, 0x00CA, 0x1F63, 0x0000, +		0x1F62, 0x00B6, 0x03CC, 0x03EF, 0x00E4, 0x1F62, 0x1FE7, +		0x1F62, 0x00A0, 0x03BA, 0x0403, 0x00FC, 0x1F62, 0x1FE3, +		0x1F64, 0x008B, 0x03A8, 0x0413, 0x0115, 0x1F63, 0x1FDE, +		0x1F65, 0x0077, 0x0395, 0x0424, 0x012E, 0x1F65, 0x1FD8, +		0x1F67, 0x0063, 0x0380, 0x0433, 0x0148, 0x1F68, 0x1FD3, +		0x1F6A, 0x004F, 0x036B, 0x0441, 0x0162, 0x1F6B, 0x1FCE, +		0x1F6D, 0x003C, 0x0355, 0x044E, 0x017D, 0x1F6F, 0x1FC8, +		0x1F70, 0x002A, 0x033E, 0x045B, 0x0197, 0x1F73, 0x1FC3, +		0x1F73, 0x0019, 0x0326, 0x0465, 0x01B3, 0x1F79, 0x1FBD, +		0x1F77, 0x0008, 0x030F, 0x046E, 0x01CE, 0x1F7F, 0x1FB7, +		0x1F7C, 0x1FF8, 0x02F6, 0x0476, 0x01E9, 0x1F86, 0x1FB1, +		0x1F80, 0x1FE9, 0x02DC, 0x047C, 0x0205, 0x1F8E, 0x1FAC, +		0x1F85, 0x1FDB, 0x02C2, 0x0481, 0x0221, 0x1F96, 0x1FA6, +		0x1F8A, 0x1FCD, 0x02A8, 0x0485, 0x023C, 0x1FA0, 0x1FA0, +		0x1F90, 0x1FC1, 0x028D, 0x0486, 0x0257, 0x1FAA, 0x1F9B, +	}, +	[HS_LT_12_16_SCALE] = { +		/* Luma */ +		0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, +		0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, +		0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, +		0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, +		0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, +		0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, +		0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, +		0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, +		0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, +		0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, +		0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, +		0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, +		0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, +		0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, +		0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, +		0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, +		0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, +		0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, +		0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, +		0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, +		0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, +		0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, +		0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, +		0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, +		0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, +		0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, +		0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, +		0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, +		0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, +		0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, +		0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, +		0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, +		/* Chroma */ +		0x1FBB, 0x1F65, 0x025E, 0x0504, 0x025E, 0x1F65, 0x1FBB, +		0x1FC3, 0x1F5D, 0x023C, 0x0503, 0x027F, 0x1F6E, 0x1FB4, +		0x1FCA, 0x1F56, 0x021B, 0x0501, 0x02A0, 0x1F78, 0x1FAC, +		0x1FD1, 0x1F50, 0x01FA, 0x04FD, 0x02C0, 0x1F83, 0x1FA5, +		0x1FD8, 0x1F4B, 0x01D9, 0x04F6, 0x02E1, 0x1F90, 0x1F9D, +		0x1FDF, 0x1F47, 0x01B8, 0x04EF, 0x0301, 0x1F9D, 0x1F95, +		0x1FE6, 0x1F43, 0x0198, 0x04E5, 0x0321, 0x1FAB, 0x1F8E, +		0x1FEC, 0x1F41, 0x0178, 0x04DA, 0x0340, 0x1FBB, 0x1F86, +		0x1FF2, 0x1F40, 0x0159, 0x04CC, 0x035E, 0x1FCC, 0x1F7F, +		0x1FF8, 0x1F40, 0x013A, 0x04BE, 0x037B, 0x1FDD, 0x1F78, +		0x1FFE, 0x1F40, 0x011B, 0x04AD, 0x0398, 0x1FF0, 0x1F72, +		0x0003, 0x1F41, 0x00FD, 0x049C, 0x03B4, 0x0004, 0x1F6B, +		0x0008, 0x1F43, 0x00E0, 0x0489, 0x03CE, 0x0019, 0x1F65, +		0x000D, 0x1F46, 0x00C4, 0x0474, 0x03E8, 0x002E, 0x1F5F, +		0x0011, 0x1F49, 0x00A9, 0x045E, 0x0400, 0x0045, 0x1F5A, +		0x0015, 0x1F4D, 0x008E, 0x0447, 0x0418, 0x005C, 0x1F55, +		0x1F4F, 0x0076, 0x043B, 0x043B, 0x0076, 0x1F4F, 0x0000, +		0x1F55, 0x005C, 0x0418, 0x0447, 0x008E, 0x1F4D, 0x0015, +		0x1F5A, 0x0045, 0x0400, 0x045E, 0x00A9, 0x1F49, 0x0011, +		0x1F5F, 0x002E, 0x03E8, 0x0474, 0x00C4, 0x1F46, 0x000D, +		0x1F65, 0x0019, 0x03CE, 0x0489, 0x00E0, 0x1F43, 0x0008, +		0x1F6B, 0x0004, 0x03B4, 0x049C, 0x00FD, 0x1F41, 0x0003, +		0x1F72, 0x1FF0, 0x0398, 0x04AD, 0x011B, 0x1F40, 0x1FFE, +		0x1F78, 0x1FDD, 0x037B, 0x04BE, 0x013A, 0x1F40, 0x1FF8, +		0x1F7F, 0x1FCC, 0x035E, 0x04CC, 0x0159, 0x1F40, 0x1FF2, +		0x1F86, 0x1FBB, 0x0340, 0x04DA, 0x0178, 0x1F41, 0x1FEC, +		0x1F8E, 0x1FAB, 0x0321, 0x04E5, 0x0198, 0x1F43, 0x1FE6, +		0x1F95, 0x1F9D, 0x0301, 0x04EF, 0x01B8, 0x1F47, 0x1FDF, +		0x1F9D, 0x1F90, 0x02E1, 0x04F6, 0x01D9, 0x1F4B, 0x1FD8, +		0x1FA5, 0x1F83, 0x02C0, 0x04FD, 0x01FA, 0x1F50, 0x1FD1, +		0x1FAC, 0x1F78, 0x02A0, 0x0501, 0x021B, 0x1F56, 0x1FCA, +		0x1FB4, 0x1F6E, 0x027F, 0x0503, 0x023C, 0x1F5D, 0x1FC3, +	}, +	[HS_LT_13_16_SCALE] = { +		/* Luma */ +		0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, +		0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, +		0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, +		0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, +		0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, +		0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, +		0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, +		0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, +		0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, +		0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, +		0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, +		0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, +		0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, +		0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, +		0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, +		0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, +		0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, +		0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, +		0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, +		0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, +		0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, +		0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, +		0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, +		0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, +		0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, +		0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, +		0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, +		0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, +		0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, +		0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, +		0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, +		0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, +		/* Chroma */ +		0x1FF4, 0x1F29, 0x022D, 0x056C, 0x022D, 0x1F29, 0x1FF4, +		0x1FFC, 0x1F26, 0x0206, 0x056A, 0x0254, 0x1F2E, 0x1FEC, +		0x0003, 0x1F24, 0x01E0, 0x0567, 0x027A, 0x1F34, 0x1FE4, +		0x000A, 0x1F23, 0x01BA, 0x0561, 0x02A2, 0x1F3B, 0x1FDB, +		0x0011, 0x1F22, 0x0194, 0x055B, 0x02C9, 0x1F43, 0x1FD2, +		0x0017, 0x1F23, 0x016F, 0x0551, 0x02F0, 0x1F4D, 0x1FC9, +		0x001D, 0x1F25, 0x014B, 0x0545, 0x0316, 0x1F58, 0x1FC0, +		0x0022, 0x1F28, 0x0127, 0x0538, 0x033C, 0x1F65, 0x1FB6, +		0x0027, 0x1F2C, 0x0104, 0x0528, 0x0361, 0x1F73, 0x1FAD, +		0x002B, 0x1F30, 0x00E2, 0x0518, 0x0386, 0x1F82, 0x1FA3, +		0x002F, 0x1F36, 0x00C2, 0x0504, 0x03AA, 0x1F92, 0x1F99, +		0x0032, 0x1F3C, 0x00A2, 0x04EF, 0x03CD, 0x1FA4, 0x1F90, +		0x0035, 0x1F42, 0x0083, 0x04D9, 0x03EF, 0x1FB8, 0x1F86, +		0x0038, 0x1F49, 0x0065, 0x04C0, 0x0410, 0x1FCD, 0x1F7D, +		0x003A, 0x1F51, 0x0048, 0x04A6, 0x0431, 0x1FE3, 0x1F73, +		0x003C, 0x1F59, 0x002D, 0x048A, 0x0450, 0x1FFA, 0x1F6A, +		0x1F5D, 0x0014, 0x048F, 0x048F, 0x0014, 0x1F5D, 0x0000, +		0x1F6A, 0x1FFA, 0x0450, 0x048A, 0x002D, 0x1F59, 0x003C, +		0x1F73, 0x1FE3, 0x0431, 0x04A6, 0x0048, 0x1F51, 0x003A, +		0x1F7D, 0x1FCD, 0x0410, 0x04C0, 0x0065, 0x1F49, 0x0038, +		0x1F86, 0x1FB8, 0x03EF, 0x04D9, 0x0083, 0x1F42, 0x0035, +		0x1F90, 0x1FA4, 0x03CD, 0x04EF, 0x00A2, 0x1F3C, 0x0032, +		0x1F99, 0x1F92, 0x03AA, 0x0504, 0x00C2, 0x1F36, 0x002F, +		0x1FA3, 0x1F82, 0x0386, 0x0518, 0x00E2, 0x1F30, 0x002B, +		0x1FAD, 0x1F73, 0x0361, 0x0528, 0x0104, 0x1F2C, 0x0027, +		0x1FB6, 0x1F65, 0x033C, 0x0538, 0x0127, 0x1F28, 0x0022, +		0x1FC0, 0x1F58, 0x0316, 0x0545, 0x014B, 0x1F25, 0x001D, +		0x1FC9, 0x1F4D, 0x02F0, 0x0551, 0x016F, 0x1F23, 0x0017, +		0x1FD2, 0x1F43, 0x02C9, 0x055B, 0x0194, 0x1F22, 0x0011, +		0x1FDB, 0x1F3B, 0x02A2, 0x0561, 0x01BA, 0x1F23, 0x000A, +		0x1FE4, 0x1F34, 0x027A, 0x0567, 0x01E0, 0x1F24, 0x0003, +		0x1FEC, 0x1F2E, 0x0254, 0x056A, 0x0206, 0x1F26, 0x1FFC, +	}, +	[HS_LT_14_16_SCALE] = { +		/* Luma */ +		0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, +		0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, +		0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, +		0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, +		0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, +		0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, +		0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, +		0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, +		0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, +		0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, +		0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, +		0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, +		0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, +		0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, +		0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, +		0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, +		0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, +		0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, +		0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, +		0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, +		0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, +		0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, +		0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, +		0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, +		0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, +		0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, +		0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, +		0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, +		0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, +		0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, +		0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, +		0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, +		/* Chroma */ +		0x002F, 0x1F0B, 0x01E7, 0x05BE, 0x01E7, 0x1F0B, 0x002F, +		0x0035, 0x1F0D, 0x01BC, 0x05BD, 0x0213, 0x1F0A, 0x0028, +		0x003A, 0x1F11, 0x0191, 0x05BA, 0x023F, 0x1F0A, 0x0021, +		0x003F, 0x1F15, 0x0167, 0x05B3, 0x026C, 0x1F0C, 0x001A, +		0x0043, 0x1F1B, 0x013E, 0x05AA, 0x0299, 0x1F0F, 0x0012, +		0x0046, 0x1F21, 0x0116, 0x05A1, 0x02C6, 0x1F13, 0x0009, +		0x0049, 0x1F28, 0x00EF, 0x0593, 0x02F4, 0x1F19, 0x0000, +		0x004C, 0x1F30, 0x00C9, 0x0584, 0x0321, 0x1F20, 0x1FF6, +		0x004E, 0x1F39, 0x00A4, 0x0572, 0x034D, 0x1F2A, 0x1FEC, +		0x004F, 0x1F43, 0x0080, 0x055E, 0x037A, 0x1F34, 0x1FE2, +		0x0050, 0x1F4D, 0x005E, 0x0548, 0x03A5, 0x1F41, 0x1FD7, +		0x0050, 0x1F57, 0x003D, 0x0531, 0x03D1, 0x1F4F, 0x1FCB, +		0x0050, 0x1F62, 0x001E, 0x0516, 0x03FB, 0x1F5F, 0x1FC0, +		0x004F, 0x1F6D, 0x0000, 0x04FA, 0x0425, 0x1F71, 0x1FB4, +		0x004E, 0x1F79, 0x1FE4, 0x04DC, 0x044D, 0x1F84, 0x1FA8, +		0x004D, 0x1F84, 0x1FCA, 0x04BC, 0x0474, 0x1F99, 0x1F9C, +		0x1F8C, 0x1FAE, 0x04C6, 0x04C6, 0x1FAE, 0x1F8C, 0x0000, +		0x1F9C, 0x1F99, 0x0474, 0x04BC, 0x1FCA, 0x1F84, 0x004D, +		0x1FA8, 0x1F84, 0x044D, 0x04DC, 0x1FE4, 0x1F79, 0x004E, +		0x1FB4, 0x1F71, 0x0425, 0x04FA, 0x0000, 0x1F6D, 0x004F, +		0x1FC0, 0x1F5F, 0x03FB, 0x0516, 0x001E, 0x1F62, 0x0050, +		0x1FCB, 0x1F4F, 0x03D1, 0x0531, 0x003D, 0x1F57, 0x0050, +		0x1FD7, 0x1F41, 0x03A5, 0x0548, 0x005E, 0x1F4D, 0x0050, +		0x1FE2, 0x1F34, 0x037A, 0x055E, 0x0080, 0x1F43, 0x004F, +		0x1FEC, 0x1F2A, 0x034D, 0x0572, 0x00A4, 0x1F39, 0x004E, +		0x1FF6, 0x1F20, 0x0321, 0x0584, 0x00C9, 0x1F30, 0x004C, +		0x0000, 0x1F19, 0x02F4, 0x0593, 0x00EF, 0x1F28, 0x0049, +		0x0009, 0x1F13, 0x02C6, 0x05A1, 0x0116, 0x1F21, 0x0046, +		0x0012, 0x1F0F, 0x0299, 0x05AA, 0x013E, 0x1F1B, 0x0043, +		0x001A, 0x1F0C, 0x026C, 0x05B3, 0x0167, 0x1F15, 0x003F, +		0x0021, 0x1F0A, 0x023F, 0x05BA, 0x0191, 0x1F11, 0x003A, +		0x0028, 0x1F0A, 0x0213, 0x05BD, 0x01BC, 0x1F0D, 0x0035, +	}, +	[HS_LT_15_16_SCALE] = { +		/* Luma */ +		0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, +		0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, +		0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, +		0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, +		0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, +		0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, +		0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, +		0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, +		0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, +		0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, +		0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, +		0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, +		0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, +		0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, +		0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, +		0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, +		0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, +		0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, +		0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, +		0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, +		0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, +		0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, +		0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, +		0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, +		0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, +		0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, +		0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, +		0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, +		0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, +		0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, +		0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, +		0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, +		/* Chroma */ +		0x005B, 0x1F0A, 0x0195, 0x060C, 0x0195, 0x1F0A, 0x005B, +		0x005D, 0x1F13, 0x0166, 0x0609, 0x01C6, 0x1F03, 0x0058, +		0x005F, 0x1F1C, 0x0138, 0x0605, 0x01F7, 0x1EFD, 0x0054, +		0x0060, 0x1F26, 0x010B, 0x05FF, 0x0229, 0x1EF8, 0x004F, +		0x0060, 0x1F31, 0x00DF, 0x05F5, 0x025C, 0x1EF5, 0x004A, +		0x0060, 0x1F3D, 0x00B5, 0x05E8, 0x028F, 0x1EF3, 0x0044, +		0x005F, 0x1F49, 0x008C, 0x05DA, 0x02C3, 0x1EF2, 0x003D, +		0x005E, 0x1F56, 0x0065, 0x05C7, 0x02F6, 0x1EF4, 0x0036, +		0x005C, 0x1F63, 0x003F, 0x05B3, 0x032B, 0x1EF7, 0x002D, +		0x0059, 0x1F71, 0x001B, 0x059D, 0x035F, 0x1EFB, 0x0024, +		0x0057, 0x1F7F, 0x1FF9, 0x0583, 0x0392, 0x1F02, 0x001A, +		0x0053, 0x1F8D, 0x1FD9, 0x0567, 0x03C5, 0x1F0B, 0x0010, +		0x0050, 0x1F9B, 0x1FBB, 0x0548, 0x03F8, 0x1F15, 0x0005, +		0x004C, 0x1FA9, 0x1F9E, 0x0528, 0x042A, 0x1F22, 0x1FF9, +		0x0048, 0x1FB7, 0x1F84, 0x0505, 0x045A, 0x1F31, 0x1FED, +		0x0043, 0x1FC5, 0x1F6C, 0x04E0, 0x048A, 0x1F42, 0x1FE0, +		0x1FD1, 0x1F50, 0x04DF, 0x04DF, 0x1F50, 0x1FD1, 0x0000, +		0x1FE0, 0x1F42, 0x048A, 0x04E0, 0x1F6C, 0x1FC5, 0x0043, +		0x1FED, 0x1F31, 0x045A, 0x0505, 0x1F84, 0x1FB7, 0x0048, +		0x1FF9, 0x1F22, 0x042A, 0x0528, 0x1F9E, 0x1FA9, 0x004C, +		0x0005, 0x1F15, 0x03F8, 0x0548, 0x1FBB, 0x1F9B, 0x0050, +		0x0010, 0x1F0B, 0x03C5, 0x0567, 0x1FD9, 0x1F8D, 0x0053, +		0x001A, 0x1F02, 0x0392, 0x0583, 0x1FF9, 0x1F7F, 0x0057, +		0x0024, 0x1EFB, 0x035F, 0x059D, 0x001B, 0x1F71, 0x0059, +		0x002D, 0x1EF7, 0x032B, 0x05B3, 0x003F, 0x1F63, 0x005C, +		0x0036, 0x1EF4, 0x02F6, 0x05C7, 0x0065, 0x1F56, 0x005E, +		0x003D, 0x1EF2, 0x02C3, 0x05DA, 0x008C, 0x1F49, 0x005F, +		0x0044, 0x1EF3, 0x028F, 0x05E8, 0x00B5, 0x1F3D, 0x0060, +		0x004A, 0x1EF5, 0x025C, 0x05F5, 0x00DF, 0x1F31, 0x0060, +		0x004F, 0x1EF8, 0x0229, 0x05FF, 0x010B, 0x1F26, 0x0060, +		0x0054, 0x1EFD, 0x01F7, 0x0605, 0x0138, 0x1F1C, 0x005F, +		0x0058, 0x1F03, 0x01C6, 0x0609, 0x0166, 0x1F13, 0x005D, +	}, +	[HS_LE_16_16_SCALE] = { +		/* Luma */ +		0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, +		0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, +		0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, +		0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, +		0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, +		0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, +		0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, +		0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, +		0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, +		0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, +		0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, +		0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, +		0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, +		0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, +		0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, +		0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, +		0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, +		0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, +		0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, +		0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, +		0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, +		0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, +		0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, +		0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, +		0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, +		0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, +		0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, +		0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, +		0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, +		0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, +		0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, +		0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, +		/* Chroma */ +		0x006E, 0x1F24, 0x013E, 0x0660, 0x013E, 0x1F24, 0x006E, +		0x006C, 0x1F33, 0x010B, 0x065D, 0x0172, 0x1F17, 0x0070, +		0x0069, 0x1F41, 0x00DA, 0x0659, 0x01A8, 0x1F0B, 0x0070, +		0x0066, 0x1F51, 0x00AA, 0x0650, 0x01DF, 0x1F00, 0x0070, +		0x0062, 0x1F61, 0x007D, 0x0644, 0x0217, 0x1EF6, 0x006F, +		0x005E, 0x1F71, 0x0051, 0x0636, 0x0250, 0x1EED, 0x006D, +		0x0059, 0x1F81, 0x0028, 0x0624, 0x028A, 0x1EE5, 0x006B, +		0x0054, 0x1F91, 0x0000, 0x060F, 0x02C5, 0x1EE0, 0x0067, +		0x004E, 0x1FA2, 0x1FDB, 0x05F6, 0x0300, 0x1EDC, 0x0063, +		0x0049, 0x1FB2, 0x1FB8, 0x05DB, 0x033B, 0x1EDA, 0x005D, +		0x0043, 0x1FC3, 0x1F98, 0x05BC, 0x0376, 0x1ED9, 0x0057, +		0x003D, 0x1FD3, 0x1F7A, 0x059B, 0x03B1, 0x1EDB, 0x004F, +		0x0036, 0x1FE2, 0x1F5E, 0x0578, 0x03EC, 0x1EDF, 0x0047, +		0x0030, 0x1FF1, 0x1F45, 0x0551, 0x0426, 0x1EE6, 0x003D, +		0x002A, 0x0000, 0x1F2E, 0x0528, 0x045F, 0x1EEE, 0x0033, +		0x0023, 0x000E, 0x1F19, 0x04FD, 0x0498, 0x1EFA, 0x0027, +		0x001B, 0x1F04, 0x04E1, 0x04E1, 0x1F04, 0x001B, 0x0000, +		0x0027, 0x1EFA, 0x0498, 0x04FD, 0x1F19, 0x000E, 0x0023, +		0x0033, 0x1EEE, 0x045F, 0x0528, 0x1F2E, 0x0000, 0x002A, +		0x003D, 0x1EE6, 0x0426, 0x0551, 0x1F45, 0x1FF1, 0x0030, +		0x0047, 0x1EDF, 0x03EC, 0x0578, 0x1F5E, 0x1FE2, 0x0036, +		0x004F, 0x1EDB, 0x03B1, 0x059B, 0x1F7A, 0x1FD3, 0x003D, +		0x0057, 0x1ED9, 0x0376, 0x05BC, 0x1F98, 0x1FC3, 0x0043, +		0x005D, 0x1EDA, 0x033B, 0x05DB, 0x1FB8, 0x1FB2, 0x0049, +		0x0063, 0x1EDC, 0x0300, 0x05F6, 0x1FDB, 0x1FA2, 0x004E, +		0x0067, 0x1EE0, 0x02C5, 0x060F, 0x0000, 0x1F91, 0x0054, +		0x006B, 0x1EE5, 0x028A, 0x0624, 0x0028, 0x1F81, 0x0059, +		0x006D, 0x1EED, 0x0250, 0x0636, 0x0051, 0x1F71, 0x005E, +		0x006F, 0x1EF6, 0x0217, 0x0644, 0x007D, 0x1F61, 0x0062, +		0x0070, 0x1F00, 0x01DF, 0x0650, 0x00AA, 0x1F51, 0x0066, +		0x0070, 0x1F0B, 0x01A8, 0x0659, 0x00DA, 0x1F41, 0x0069, +		0x0070, 0x1F17, 0x0172, 0x065D, 0x010B, 0x1F33, 0x006C, +	}, +}; + +/* vertical scaler coefficients */ +enum { +	VS_UP_SCALE = 0, +	VS_LT_9_16_SCALE, +	VS_LT_10_16_SCALE, +	VS_LT_11_16_SCALE, +	VS_LT_12_16_SCALE, +	VS_LT_13_16_SCALE, +	VS_LT_14_16_SCALE, +	VS_LT_15_16_SCALE, +	VS_LT_16_16_SCALE, +	VS_1_TO_1_SCALE, +}; + +static const u16 scaler_vs_coeffs[15][SC_NUM_PHASES * 2 * SC_V_NUM_TAPS] = { +	[VS_UP_SCALE] = { +		/* Luma */ +		0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, +		0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, +		0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, +		0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, +		0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, +		0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, +		0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, +		0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, +		0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, +		0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, +		0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, +		0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, +		0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, +		0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, +		0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, +		0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, +		0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, +		0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, +		0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, +		0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, +		0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, +		0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, +		0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, +		0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, +		0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, +		0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, +		0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, +		0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, +		0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, +		0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, +		0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, +		0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, +		/* Chroma */ +		0x1FD1, 0x00B1, 0x06FC, 0x00B1, 0x1FD1, +		0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, +		0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, +		0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, +		0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, +		0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, +		0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, +		0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, +		0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, +		0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, +		0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, +		0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, +		0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, +		0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, +		0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, +		0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, +		0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, +		0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, +		0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, +		0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, +		0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, +		0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, +		0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, +		0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, +		0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, +		0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, +		0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, +		0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, +		0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, +		0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, +		0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, +		0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, +	}, +	[VS_LT_9_16_SCALE] = { +		/* Luma */ +		0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, +		0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, +		0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, +		0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, +		0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, +		0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, +		0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, +		0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, +		0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, +		0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, +		0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, +		0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, +		0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, +		0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, +		0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, +		0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, +		0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, +		0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, +		0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, +		0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, +		0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, +		0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, +		0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, +		0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, +		0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, +		0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, +		0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, +		0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, +		0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, +		0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, +		0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, +		0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, +		/* Chroma */ +		0x001C, 0x01F6, 0x03DC, 0x01F6, 0x001C, +		0x0018, 0x01DF, 0x03DB, 0x020C, 0x0022, +		0x0013, 0x01C9, 0x03D9, 0x0223, 0x0028, +		0x000F, 0x01B3, 0x03D6, 0x023A, 0x002E, +		0x000C, 0x019D, 0x03D2, 0x0250, 0x0035, +		0x0009, 0x0188, 0x03CC, 0x0266, 0x003D, +		0x0006, 0x0173, 0x03C5, 0x027D, 0x0045, +		0x0004, 0x015E, 0x03BD, 0x0293, 0x004E, +		0x0002, 0x014A, 0x03B4, 0x02A8, 0x0058, +		0x0000, 0x0136, 0x03AA, 0x02BE, 0x0062, +		0x1FFF, 0x0123, 0x039E, 0x02D3, 0x006D, +		0x1FFE, 0x0110, 0x0392, 0x02E8, 0x0078, +		0x1FFD, 0x00FE, 0x0384, 0x02FC, 0x0085, +		0x1FFD, 0x00ED, 0x0376, 0x030F, 0x0091, +		0x1FFC, 0x00DC, 0x0367, 0x0322, 0x009F, +		0x1FFC, 0x00CC, 0x0357, 0x0334, 0x00AD, +		0x00BC, 0x0344, 0x0344, 0x00BC, 0x0000, +		0x00AD, 0x0334, 0x0357, 0x00CC, 0x1FFC, +		0x009F, 0x0322, 0x0367, 0x00DC, 0x1FFC, +		0x0091, 0x030F, 0x0376, 0x00ED, 0x1FFD, +		0x0085, 0x02FC, 0x0384, 0x00FE, 0x1FFD, +		0x0078, 0x02E8, 0x0392, 0x0110, 0x1FFE, +		0x006D, 0x02D3, 0x039E, 0x0123, 0x1FFF, +		0x0062, 0x02BE, 0x03AA, 0x0136, 0x0000, +		0x0058, 0x02A8, 0x03B4, 0x014A, 0x0002, +		0x004E, 0x0293, 0x03BD, 0x015E, 0x0004, +		0x0045, 0x027D, 0x03C5, 0x0173, 0x0006, +		0x003D, 0x0266, 0x03CC, 0x0188, 0x0009, +		0x0035, 0x0250, 0x03D2, 0x019D, 0x000C, +		0x002E, 0x023A, 0x03D6, 0x01B3, 0x000F, +		0x0028, 0x0223, 0x03D9, 0x01C9, 0x0013, +		0x0022, 0x020C, 0x03DB, 0x01DF, 0x0018, +	}, +	[VS_LT_10_16_SCALE] = { +		/* Luma */ +		0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, +		0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, +		0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, +		0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, +		0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, +		0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, +		0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, +		0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, +		0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, +		0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, +		0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, +		0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, +		0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, +		0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, +		0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, +		0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, +		0x0094, 0x036C, 0x036C, 0x0094, 0x0000, +		0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, +		0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, +		0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, +		0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, +		0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, +		0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, +		0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, +		0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, +		0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, +		0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, +		0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, +		0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, +		0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, +		0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, +		0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, +		/* Chroma */ +		0x0003, 0x01E9, 0x0428, 0x01E9, 0x0003, +		0x0000, 0x01D0, 0x0426, 0x0203, 0x0007, +		0x1FFD, 0x01B7, 0x0424, 0x021C, 0x000C, +		0x1FFB, 0x019E, 0x0420, 0x0236, 0x0011, +		0x1FF9, 0x0186, 0x041A, 0x0250, 0x0017, +		0x1FF7, 0x016E, 0x0414, 0x026A, 0x001D, +		0x1FF6, 0x0157, 0x040B, 0x0284, 0x0024, +		0x1FF5, 0x0140, 0x0401, 0x029E, 0x002C, +		0x1FF4, 0x012A, 0x03F6, 0x02B7, 0x0035, +		0x1FF4, 0x0115, 0x03E9, 0x02D0, 0x003E, +		0x1FF4, 0x0100, 0x03DB, 0x02E9, 0x0048, +		0x1FF4, 0x00EC, 0x03CC, 0x0301, 0x0053, +		0x1FF4, 0x00D9, 0x03BC, 0x0318, 0x005F, +		0x1FF5, 0x00C7, 0x03AA, 0x032F, 0x006B, +		0x1FF6, 0x00B5, 0x0398, 0x0345, 0x0078, +		0x1FF6, 0x00A5, 0x0384, 0x035B, 0x0086, +		0x0094, 0x036C, 0x036C, 0x0094, 0x0000, +		0x0086, 0x035B, 0x0384, 0x00A5, 0x1FF6, +		0x0078, 0x0345, 0x0398, 0x00B5, 0x1FF6, +		0x006B, 0x032F, 0x03AA, 0x00C7, 0x1FF5, +		0x005F, 0x0318, 0x03BC, 0x00D9, 0x1FF4, +		0x0053, 0x0301, 0x03CC, 0x00EC, 0x1FF4, +		0x0048, 0x02E9, 0x03DB, 0x0100, 0x1FF4, +		0x003E, 0x02D0, 0x03E9, 0x0115, 0x1FF4, +		0x0035, 0x02B7, 0x03F6, 0x012A, 0x1FF4, +		0x002C, 0x029E, 0x0401, 0x0140, 0x1FF5, +		0x0024, 0x0284, 0x040B, 0x0157, 0x1FF6, +		0x001D, 0x026A, 0x0414, 0x016E, 0x1FF7, +		0x0017, 0x0250, 0x041A, 0x0186, 0x1FF9, +		0x0011, 0x0236, 0x0420, 0x019E, 0x1FFB, +		0x000C, 0x021C, 0x0424, 0x01B7, 0x1FFD, +		0x0007, 0x0203, 0x0426, 0x01D0, 0x0000, +	}, +	[VS_LT_11_16_SCALE] = { +		/* Luma */ +		0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, +		0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, +		0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, +		0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, +		0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, +		0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, +		0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, +		0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, +		0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, +		0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, +		0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, +		0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, +		0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, +		0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, +		0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, +		0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, +		0x0069, 0x0397, 0x0397, 0x0069, 0x0000, +		0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, +		0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, +		0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, +		0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, +		0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, +		0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, +		0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, +		0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, +		0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, +		0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, +		0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, +		0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, +		0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, +		0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, +		0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, +		/* Chroma */ +		0x1FEC, 0x01D6, 0x047C, 0x01D6, 0x1FEC, +		0x1FEA, 0x01BA, 0x047B, 0x01F3, 0x1FEE, +		0x1FE9, 0x019D, 0x0478, 0x0211, 0x1FF1, +		0x1FE8, 0x0182, 0x0473, 0x022E, 0x1FF5, +		0x1FE8, 0x0167, 0x046C, 0x024C, 0x1FF9, +		0x1FE8, 0x014D, 0x0464, 0x026A, 0x1FFD, +		0x1FE8, 0x0134, 0x0459, 0x0288, 0x0003, +		0x1FE9, 0x011B, 0x044D, 0x02A6, 0x0009, +		0x1FE9, 0x0104, 0x0440, 0x02C3, 0x0010, +		0x1FEA, 0x00ED, 0x0430, 0x02E1, 0x0018, +		0x1FEB, 0x00D7, 0x0420, 0x02FD, 0x0021, +		0x1FED, 0x00C2, 0x040D, 0x0319, 0x002B, +		0x1FEE, 0x00AE, 0x03F9, 0x0336, 0x0035, +		0x1FF0, 0x009C, 0x03E3, 0x0350, 0x0041, +		0x1FF1, 0x008A, 0x03CD, 0x036B, 0x004D, +		0x1FF3, 0x0079, 0x03B5, 0x0384, 0x005B, +		0x0069, 0x0397, 0x0397, 0x0069, 0x0000, +		0x005B, 0x0384, 0x03B5, 0x0079, 0x1FF3, +		0x004D, 0x036B, 0x03CD, 0x008A, 0x1FF1, +		0x0041, 0x0350, 0x03E3, 0x009C, 0x1FF0, +		0x0035, 0x0336, 0x03F9, 0x00AE, 0x1FEE, +		0x002B, 0x0319, 0x040D, 0x00C2, 0x1FED, +		0x0021, 0x02FD, 0x0420, 0x00D7, 0x1FEB, +		0x0018, 0x02E1, 0x0430, 0x00ED, 0x1FEA, +		0x0010, 0x02C3, 0x0440, 0x0104, 0x1FE9, +		0x0009, 0x02A6, 0x044D, 0x011B, 0x1FE9, +		0x0003, 0x0288, 0x0459, 0x0134, 0x1FE8, +		0x1FFD, 0x026A, 0x0464, 0x014D, 0x1FE8, +		0x1FF9, 0x024C, 0x046C, 0x0167, 0x1FE8, +		0x1FF5, 0x022E, 0x0473, 0x0182, 0x1FE8, +		0x1FF1, 0x0211, 0x0478, 0x019D, 0x1FE9, +		0x1FEE, 0x01F3, 0x047B, 0x01BA, 0x1FEA, +	}, +	[VS_LT_12_16_SCALE] = { +		/* Luma */ +		0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, +		0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, +		0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, +		0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, +		0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, +		0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, +		0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, +		0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, +		0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, +		0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, +		0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, +		0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, +		0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, +		0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, +		0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, +		0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, +		0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, +		0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, +		0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, +		0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, +		0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, +		0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, +		0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, +		0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, +		0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, +		0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, +		0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, +		0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, +		0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, +		0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, +		0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, +		0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, +		/* Chroma */ +		0x1FD8, 0x01BC, 0x04D8, 0x01BC, 0x1FD8, +		0x1FD8, 0x019C, 0x04D8, 0x01DC, 0x1FD8, +		0x1FD8, 0x017D, 0x04D4, 0x01FE, 0x1FD9, +		0x1FD9, 0x015E, 0x04CF, 0x0220, 0x1FDA, +		0x1FDB, 0x0141, 0x04C7, 0x0241, 0x1FDC, +		0x1FDC, 0x0125, 0x04BC, 0x0264, 0x1FDF, +		0x1FDE, 0x0109, 0x04B0, 0x0286, 0x1FE3, +		0x1FE0, 0x00EF, 0x04A1, 0x02A9, 0x1FE7, +		0x1FE2, 0x00D6, 0x0491, 0x02CB, 0x1FEC, +		0x1FE4, 0x00BE, 0x047E, 0x02EE, 0x1FF2, +		0x1FE6, 0x00A7, 0x046A, 0x030F, 0x1FFA, +		0x1FE9, 0x0092, 0x0453, 0x0330, 0x0002, +		0x1FEB, 0x007E, 0x043B, 0x0351, 0x000B, +		0x1FED, 0x006B, 0x0421, 0x0372, 0x0015, +		0x1FEF, 0x005A, 0x0406, 0x0391, 0x0020, +		0x1FF1, 0x0049, 0x03EA, 0x03AF, 0x002D, +		0x003A, 0x03C6, 0x03C6, 0x003A, 0x0000, +		0x002D, 0x03AF, 0x03EA, 0x0049, 0x1FF1, +		0x0020, 0x0391, 0x0406, 0x005A, 0x1FEF, +		0x0015, 0x0372, 0x0421, 0x006B, 0x1FED, +		0x000B, 0x0351, 0x043B, 0x007E, 0x1FEB, +		0x0002, 0x0330, 0x0453, 0x0092, 0x1FE9, +		0x1FFA, 0x030F, 0x046A, 0x00A7, 0x1FE6, +		0x1FF2, 0x02EE, 0x047E, 0x00BE, 0x1FE4, +		0x1FEC, 0x02CB, 0x0491, 0x00D6, 0x1FE2, +		0x1FE7, 0x02A9, 0x04A1, 0x00EF, 0x1FE0, +		0x1FE3, 0x0286, 0x04B0, 0x0109, 0x1FDE, +		0x1FDF, 0x0264, 0x04BC, 0x0125, 0x1FDC, +		0x1FDC, 0x0241, 0x04C7, 0x0141, 0x1FDB, +		0x1FDA, 0x0220, 0x04CF, 0x015E, 0x1FD9, +		0x1FD9, 0x01FE, 0x04D4, 0x017D, 0x1FD8, +		0x1FD8, 0x01DC, 0x04D8, 0x019C, 0x1FD8, +	}, +	[VS_LT_13_16_SCALE] = { +		/* Luma */ +		0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, +		0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, +		0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, +		0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, +		0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, +		0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, +		0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, +		0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, +		0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, +		0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, +		0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, +		0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, +		0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, +		0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, +		0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, +		0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, +		0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, +		0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, +		0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, +		0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, +		0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, +		0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, +		0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, +		0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, +		0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, +		0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, +		0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, +		0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, +		0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, +		0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, +		0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, +		0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, +		/* Chroma */ +		0x1FC8, 0x0199, 0x053E, 0x0199, 0x1FC8, +		0x1FCA, 0x0175, 0x053E, 0x01BD, 0x1FC6, +		0x1FCD, 0x0153, 0x0539, 0x01E2, 0x1FC5, +		0x1FCF, 0x0132, 0x0532, 0x0209, 0x1FC4, +		0x1FD2, 0x0112, 0x0529, 0x022F, 0x1FC4, +		0x1FD5, 0x00F4, 0x051C, 0x0256, 0x1FC5, +		0x1FD8, 0x00D7, 0x050D, 0x027E, 0x1FC6, +		0x1FDC, 0x00BB, 0x04FB, 0x02A6, 0x1FC8, +		0x1FDF, 0x00A1, 0x04E7, 0x02CE, 0x1FCB, +		0x1FE2, 0x0089, 0x04D1, 0x02F5, 0x1FCF, +		0x1FE5, 0x0072, 0x04B8, 0x031D, 0x1FD4, +		0x1FE8, 0x005D, 0x049E, 0x0344, 0x1FD9, +		0x1FEB, 0x0049, 0x0480, 0x036B, 0x1FE1, +		0x1FEE, 0x0037, 0x0462, 0x0390, 0x1FE9, +		0x1FF0, 0x0026, 0x0442, 0x03B6, 0x1FF2, +		0x1FF2, 0x0017, 0x0420, 0x03DA, 0x1FFD, +		0x0009, 0x03F7, 0x03F7, 0x0009, 0x0000, +		0x1FFD, 0x03DA, 0x0420, 0x0017, 0x1FF2, +		0x1FF2, 0x03B6, 0x0442, 0x0026, 0x1FF0, +		0x1FE9, 0x0390, 0x0462, 0x0037, 0x1FEE, +		0x1FE1, 0x036B, 0x0480, 0x0049, 0x1FEB, +		0x1FD9, 0x0344, 0x049E, 0x005D, 0x1FE8, +		0x1FD4, 0x031D, 0x04B8, 0x0072, 0x1FE5, +		0x1FCF, 0x02F5, 0x04D1, 0x0089, 0x1FE2, +		0x1FCB, 0x02CE, 0x04E7, 0x00A1, 0x1FDF, +		0x1FC8, 0x02A6, 0x04FB, 0x00BB, 0x1FDC, +		0x1FC6, 0x027E, 0x050D, 0x00D7, 0x1FD8, +		0x1FC5, 0x0256, 0x051C, 0x00F4, 0x1FD5, +		0x1FC4, 0x022F, 0x0529, 0x0112, 0x1FD2, +		0x1FC4, 0x0209, 0x0532, 0x0132, 0x1FCF, +		0x1FC5, 0x01E2, 0x0539, 0x0153, 0x1FCD, +		0x1FC6, 0x01BD, 0x053E, 0x0175, 0x1FCA, +	}, +	[VS_LT_14_16_SCALE] = { +		/* Luma */ +		0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, +		0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, +		0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, +		0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, +		0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, +		0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, +		0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, +		0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, +		0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, +		0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, +		0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, +		0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, +		0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, +		0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, +		0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, +		0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, +		0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, +		0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, +		0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, +		0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, +		0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, +		0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, +		0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, +		0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, +		0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, +		0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, +		0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, +		0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, +		0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, +		0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, +		0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, +		0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, +		/* Chroma */ +		0x1FBF, 0x016C, 0x05AA, 0x016C, 0x1FBF, +		0x1FC3, 0x0146, 0x05A8, 0x0194, 0x1FBB, +		0x1FC7, 0x0121, 0x05A3, 0x01BD, 0x1FB8, +		0x1FCB, 0x00FD, 0x059B, 0x01E8, 0x1FB5, +		0x1FD0, 0x00DC, 0x058F, 0x0213, 0x1FB2, +		0x1FD4, 0x00BC, 0x0580, 0x0240, 0x1FB0, +		0x1FD8, 0x009E, 0x056E, 0x026D, 0x1FAF, +		0x1FDC, 0x0082, 0x055A, 0x029A, 0x1FAE, +		0x1FE0, 0x0067, 0x0542, 0x02C9, 0x1FAE, +		0x1FE4, 0x004F, 0x0528, 0x02F6, 0x1FAF, +		0x1FE8, 0x0038, 0x050A, 0x0325, 0x1FB1, +		0x1FEB, 0x0024, 0x04EB, 0x0352, 0x1FB4, +		0x1FEE, 0x0011, 0x04C8, 0x0380, 0x1FB9, +		0x1FF1, 0x0000, 0x04A4, 0x03AC, 0x1FBF, +		0x1FF4, 0x1FF1, 0x047D, 0x03D8, 0x1FC6, +		0x1FF6, 0x1FE4, 0x0455, 0x0403, 0x1FCE, +		0x1FD8, 0x0428, 0x0428, 0x1FD8, 0x0000, +		0x1FCE, 0x0403, 0x0455, 0x1FE4, 0x1FF6, +		0x1FC6, 0x03D8, 0x047D, 0x1FF1, 0x1FF4, +		0x1FBF, 0x03AC, 0x04A4, 0x0000, 0x1FF1, +		0x1FB9, 0x0380, 0x04C8, 0x0011, 0x1FEE, +		0x1FB4, 0x0352, 0x04EB, 0x0024, 0x1FEB, +		0x1FB1, 0x0325, 0x050A, 0x0038, 0x1FE8, +		0x1FAF, 0x02F6, 0x0528, 0x004F, 0x1FE4, +		0x1FAE, 0x02C9, 0x0542, 0x0067, 0x1FE0, +		0x1FAE, 0x029A, 0x055A, 0x0082, 0x1FDC, +		0x1FAF, 0x026D, 0x056E, 0x009E, 0x1FD8, +		0x1FB0, 0x0240, 0x0580, 0x00BC, 0x1FD4, +		0x1FB2, 0x0213, 0x058F, 0x00DC, 0x1FD0, +		0x1FB5, 0x01E8, 0x059B, 0x00FD, 0x1FCB, +		0x1FB8, 0x01BD, 0x05A3, 0x0121, 0x1FC7, +		0x1FBB, 0x0194, 0x05A8, 0x0146, 0x1FC3, +	}, +	[VS_LT_15_16_SCALE] = { +		/* Luma */ +		0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, +		0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, +		0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, +		0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, +		0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, +		0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, +		0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, +		0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, +		0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, +		0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, +		0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, +		0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, +		0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, +		0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, +		0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, +		0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, +		0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, +		0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, +		0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, +		0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, +		0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, +		0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, +		0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, +		0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, +		0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, +		0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, +		0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, +		0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, +		0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, +		0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, +		0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, +		0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, +		/* Chroma */ +		0x1FBD, 0x0136, 0x061A, 0x0136, 0x1FBD, +		0x1FC3, 0x010D, 0x0617, 0x0161, 0x1FB8, +		0x1FC9, 0x00E6, 0x0611, 0x018E, 0x1FB2, +		0x1FCE, 0x00C1, 0x0607, 0x01BD, 0x1FAD, +		0x1FD4, 0x009E, 0x05F9, 0x01ED, 0x1FA8, +		0x1FD9, 0x007D, 0x05E8, 0x021F, 0x1FA3, +		0x1FDE, 0x005E, 0x05D3, 0x0252, 0x1F9F, +		0x1FE2, 0x0042, 0x05BC, 0x0285, 0x1F9B, +		0x1FE7, 0x0029, 0x059F, 0x02B9, 0x1F98, +		0x1FEA, 0x0011, 0x0580, 0x02EF, 0x1F96, +		0x1FEE, 0x1FFC, 0x055D, 0x0324, 0x1F95, +		0x1FF1, 0x1FE9, 0x0538, 0x0359, 0x1F95, +		0x1FF4, 0x1FD8, 0x0510, 0x038E, 0x1F96, +		0x1FF7, 0x1FC9, 0x04E5, 0x03C2, 0x1F99, +		0x1FF9, 0x1FBD, 0x04B8, 0x03F5, 0x1F9D, +		0x1FFB, 0x1FB2, 0x0489, 0x0428, 0x1FA2, +		0x1FAA, 0x0456, 0x0456, 0x1FAA, 0x0000, +		0x1FA2, 0x0428, 0x0489, 0x1FB2, 0x1FFB, +		0x1F9D, 0x03F5, 0x04B8, 0x1FBD, 0x1FF9, +		0x1F99, 0x03C2, 0x04E5, 0x1FC9, 0x1FF7, +		0x1F96, 0x038E, 0x0510, 0x1FD8, 0x1FF4, +		0x1F95, 0x0359, 0x0538, 0x1FE9, 0x1FF1, +		0x1F95, 0x0324, 0x055D, 0x1FFC, 0x1FEE, +		0x1F96, 0x02EF, 0x0580, 0x0011, 0x1FEA, +		0x1F98, 0x02B9, 0x059F, 0x0029, 0x1FE7, +		0x1F9B, 0x0285, 0x05BC, 0x0042, 0x1FE2, +		0x1F9F, 0x0252, 0x05D3, 0x005E, 0x1FDE, +		0x1FA3, 0x021F, 0x05E8, 0x007D, 0x1FD9, +		0x1FA8, 0x01ED, 0x05F9, 0x009E, 0x1FD4, +		0x1FAD, 0x01BD, 0x0607, 0x00C1, 0x1FCE, +		0x1FB2, 0x018E, 0x0611, 0x00E6, 0x1FC9, +		0x1FB8, 0x0161, 0x0617, 0x010D, 0x1FC3, +	}, +	[VS_LT_16_16_SCALE] = { +		/* Luma */ +		0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, +		0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, +		0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, +		0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, +		0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, +		0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, +		0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, +		0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, +		0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, +		0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, +		0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, +		0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, +		0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, +		0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, +		0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, +		0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, +		0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, +		0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, +		0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, +		0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, +		0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, +		0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, +		0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, +		0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, +		0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, +		0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, +		0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, +		0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, +		0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, +		0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, +		0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, +		0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, +		/* Chroma */ +		0x1FC3, 0x00F8, 0x068A, 0x00F8, 0x1FC3, +		0x1FCA, 0x00CC, 0x0689, 0x0125, 0x1FBC, +		0x1FD1, 0x00A3, 0x0681, 0x0156, 0x1FB5, +		0x1FD7, 0x007D, 0x0676, 0x0188, 0x1FAE, +		0x1FDD, 0x005A, 0x0666, 0x01BD, 0x1FA6, +		0x1FE3, 0x0039, 0x0652, 0x01F3, 0x1F9F, +		0x1FE8, 0x001B, 0x0639, 0x022C, 0x1F98, +		0x1FEC, 0x0000, 0x061D, 0x0265, 0x1F92, +		0x1FF0, 0x1FE8, 0x05FC, 0x02A0, 0x1F8C, +		0x1FF4, 0x1FD2, 0x05D7, 0x02DC, 0x1F87, +		0x1FF7, 0x1FBF, 0x05AF, 0x0319, 0x1F82, +		0x1FFA, 0x1FAF, 0x0583, 0x0356, 0x1F7E, +		0x1FFC, 0x1FA1, 0x0554, 0x0393, 0x1F7C, +		0x1FFE, 0x1F95, 0x0523, 0x03CF, 0x1F7B, +		0x0000, 0x1F8C, 0x04EE, 0x040B, 0x1F7B, +		0x0001, 0x1F85, 0x04B8, 0x0446, 0x1F7C, +		0x1F80, 0x0480, 0x0480, 0x1F80, 0x0000, +		0x1F7C, 0x0446, 0x04B8, 0x1F85, 0x0001, +		0x1F7B, 0x040B, 0x04EE, 0x1F8C, 0x0000, +		0x1F7B, 0x03CF, 0x0523, 0x1F95, 0x1FFE, +		0x1F7C, 0x0393, 0x0554, 0x1FA1, 0x1FFC, +		0x1F7E, 0x0356, 0x0583, 0x1FAF, 0x1FFA, +		0x1F82, 0x0319, 0x05AF, 0x1FBF, 0x1FF7, +		0x1F87, 0x02DC, 0x05D7, 0x1FD2, 0x1FF4, +		0x1F8C, 0x02A0, 0x05FC, 0x1FE8, 0x1FF0, +		0x1F92, 0x0265, 0x061D, 0x0000, 0x1FEC, +		0x1F98, 0x022C, 0x0639, 0x001B, 0x1FE8, +		0x1F9F, 0x01F3, 0x0652, 0x0039, 0x1FE3, +		0x1FA6, 0x01BD, 0x0666, 0x005A, 0x1FDD, +		0x1FAE, 0x0188, 0x0676, 0x007D, 0x1FD7, +		0x1FB5, 0x0156, 0x0681, 0x00A3, 0x1FD1, +		0x1FBC, 0x0125, 0x0689, 0x00CC, 0x1FCA, +	}, +	[VS_1_TO_1_SCALE] = { +		/* Luma */ +		0x0000, 0x0000, 0x0800, 0x0000, 0x0000, +		0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, +		0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, +		0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, +		0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, +		0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, +		0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, +		0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, +		0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, +		0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, +		0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, +		0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, +		0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, +		0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, +		0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, +		0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, +		0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, +		0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, +		0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, +		0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, +		0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, +		0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, +		0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, +		0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, +		0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, +		0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, +		0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, +		0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, +		0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, +		0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, +		0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, +		0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, +		/* Chroma */ +		0x0000, 0x0000, 0x0800, 0x0000, 0x0000, +		0x1FD8, 0x0085, 0x06F9, 0x00E1, 0x1FC9, +		0x1FDF, 0x005B, 0x06F2, 0x0114, 0x1FC0, +		0x1FE5, 0x0035, 0x06E5, 0x014A, 0x1FB7, +		0x1FEB, 0x0012, 0x06D3, 0x0182, 0x1FAE, +		0x1FF1, 0x1FF3, 0x06BA, 0x01BD, 0x1FA5, +		0x1FF5, 0x1FD7, 0x069D, 0x01FB, 0x1F9C, +		0x1FF9, 0x1FBE, 0x067C, 0x023A, 0x1F93, +		0x1FFD, 0x1FA8, 0x0656, 0x027B, 0x1F8A, +		0x0000, 0x1F95, 0x062B, 0x02BF, 0x1F81, +		0x0002, 0x1F86, 0x05FC, 0x0303, 0x1F79, +		0x0004, 0x1F79, 0x05CA, 0x0347, 0x1F72, +		0x0005, 0x1F6F, 0x0594, 0x038D, 0x1F6B, +		0x0006, 0x1F67, 0x055B, 0x03D2, 0x1F66, +		0x0007, 0x1F62, 0x051E, 0x0417, 0x1F62, +		0x0007, 0x1F5F, 0x04DF, 0x045C, 0x1F5F, +		0x1F5E, 0x04A2, 0x04A2, 0x1F5E, 0x0000, +		0x1F5F, 0x045C, 0x04DF, 0x1F5F, 0x0007, +		0x1F62, 0x0417, 0x051E, 0x1F62, 0x0007, +		0x1F66, 0x03D2, 0x055B, 0x1F67, 0x0006, +		0x1F6B, 0x038D, 0x0594, 0x1F6F, 0x0005, +		0x1F72, 0x0347, 0x05CA, 0x1F79, 0x0004, +		0x1F79, 0x0303, 0x05FC, 0x1F86, 0x0002, +		0x1F81, 0x02BF, 0x062B, 0x1F95, 0x0000, +		0x1F8A, 0x027B, 0x0656, 0x1FA8, 0x1FFD, +		0x1F93, 0x023A, 0x067C, 0x1FBE, 0x1FF9, +		0x1F9C, 0x01FB, 0x069D, 0x1FD7, 0x1FF5, +		0x1FA5, 0x01BD, 0x06BA, 0x1FF3, 0x1FF1, +		0x1FAE, 0x0182, 0x06D3, 0x0012, 0x1FEB, +		0x1FB7, 0x014A, 0x06E5, 0x0035, 0x1FE5, +		0x1FC0, 0x0114, 0x06F2, 0x005B, 0x1FDF, +		0x1FC9, 0x00E1, 0x06F9, 0x0085, 0x1FD8, +	}, +}; +#endif diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c new file mode 100644 index 00000000000..a51a0135980 --- /dev/null +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -0,0 +1,912 @@ +/* + * VPDMA helper library + * + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include "vpdma.h" +#include "vpdma_priv.h" + +#define VPDMA_FIRMWARE	"vpdma-1b8.bin" + +const struct vpdma_data_format vpdma_yuv_fmts[] = { +	[VPDMA_DATA_FMT_Y444] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_Y444, +		.depth		= 8, +	}, +	[VPDMA_DATA_FMT_Y422] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_Y422, +		.depth		= 8, +	}, +	[VPDMA_DATA_FMT_Y420] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_Y420, +		.depth		= 8, +	}, +	[VPDMA_DATA_FMT_C444] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_C444, +		.depth		= 8, +	}, +	[VPDMA_DATA_FMT_C422] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_C422, +		.depth		= 8, +	}, +	[VPDMA_DATA_FMT_C420] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_C420, +		.depth		= 4, +	}, +	[VPDMA_DATA_FMT_YC422] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_YC422, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_YC444] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_YC444, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_CY422] = { +		.type		= VPDMA_DATA_FMT_TYPE_YUV, +		.data_type	= DATA_TYPE_CY422, +		.depth		= 16, +	}, +}; + +const struct vpdma_data_format vpdma_rgb_fmts[] = { +	[VPDMA_DATA_FMT_RGB565] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGB16_565, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ARGB16_1555] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ARGB_1555, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ARGB16] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ARGB_4444, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_RGBA16_5551] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGBA_5551, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_RGBA16] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGBA_4444, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ARGB24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ARGB24_6666, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_RGB24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGB24_888, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_ARGB32] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ARGB32_8888, +		.depth		= 32, +	}, +	[VPDMA_DATA_FMT_RGBA24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGBA24_6666, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_RGBA32] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_RGBA32_8888, +		.depth		= 32, +	}, +	[VPDMA_DATA_FMT_BGR565] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGR16_565, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ABGR16_1555] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ABGR_1555, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ABGR16] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ABGR_4444, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_BGRA16_5551] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGRA_5551, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_BGRA16] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGRA_4444, +		.depth		= 16, +	}, +	[VPDMA_DATA_FMT_ABGR24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ABGR24_6666, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_BGR24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGR24_888, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_ABGR32] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_ABGR32_8888, +		.depth		= 32, +	}, +	[VPDMA_DATA_FMT_BGRA24] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGRA24_6666, +		.depth		= 24, +	}, +	[VPDMA_DATA_FMT_BGRA32] = { +		.type		= VPDMA_DATA_FMT_TYPE_RGB, +		.data_type	= DATA_TYPE_BGRA32_8888, +		.depth		= 32, +	}, +}; + +const struct vpdma_data_format vpdma_misc_fmts[] = { +	[VPDMA_DATA_FMT_MV] = { +		.type		= VPDMA_DATA_FMT_TYPE_MISC, +		.data_type	= DATA_TYPE_MV, +		.depth		= 4, +	}, +}; + +struct vpdma_channel_info { +	int num;		/* VPDMA channel number */ +	int cstat_offset;	/* client CSTAT register offset */ +}; + +static const struct vpdma_channel_info chan_info[] = { +	[VPE_CHAN_LUMA1_IN] = { +		.num		= VPE_CHAN_NUM_LUMA1_IN, +		.cstat_offset	= VPDMA_DEI_LUMA1_CSTAT, +	}, +	[VPE_CHAN_CHROMA1_IN] = { +		.num		= VPE_CHAN_NUM_CHROMA1_IN, +		.cstat_offset	= VPDMA_DEI_CHROMA1_CSTAT, +	}, +	[VPE_CHAN_LUMA2_IN] = { +		.num		= VPE_CHAN_NUM_LUMA2_IN, +		.cstat_offset	= VPDMA_DEI_LUMA2_CSTAT, +	}, +	[VPE_CHAN_CHROMA2_IN] = { +		.num		= VPE_CHAN_NUM_CHROMA2_IN, +		.cstat_offset	= VPDMA_DEI_CHROMA2_CSTAT, +	}, +	[VPE_CHAN_LUMA3_IN] = { +		.num		= VPE_CHAN_NUM_LUMA3_IN, +		.cstat_offset	= VPDMA_DEI_LUMA3_CSTAT, +	}, +	[VPE_CHAN_CHROMA3_IN] = { +		.num		= VPE_CHAN_NUM_CHROMA3_IN, +		.cstat_offset	= VPDMA_DEI_CHROMA3_CSTAT, +	}, +	[VPE_CHAN_MV_IN] = { +		.num		= VPE_CHAN_NUM_MV_IN, +		.cstat_offset	= VPDMA_DEI_MV_IN_CSTAT, +	}, +	[VPE_CHAN_MV_OUT] = { +		.num		= VPE_CHAN_NUM_MV_OUT, +		.cstat_offset	= VPDMA_DEI_MV_OUT_CSTAT, +	}, +	[VPE_CHAN_LUMA_OUT] = { +		.num		= VPE_CHAN_NUM_LUMA_OUT, +		.cstat_offset	= VPDMA_VIP_UP_Y_CSTAT, +	}, +	[VPE_CHAN_CHROMA_OUT] = { +		.num		= VPE_CHAN_NUM_CHROMA_OUT, +		.cstat_offset	= VPDMA_VIP_UP_UV_CSTAT, +	}, +	[VPE_CHAN_RGB_OUT] = { +		.num		= VPE_CHAN_NUM_RGB_OUT, +		.cstat_offset	= VPDMA_VIP_UP_Y_CSTAT, +	}, +}; + +static u32 read_reg(struct vpdma_data *vpdma, int offset) +{ +	return ioread32(vpdma->base + offset); +} + +static void write_reg(struct vpdma_data *vpdma, int offset, u32 value) +{ +	iowrite32(value, vpdma->base + offset); +} + +static int read_field_reg(struct vpdma_data *vpdma, int offset, +		u32 mask, int shift) +{ +	return (read_reg(vpdma, offset) & (mask << shift)) >> shift; +} + +static void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field, +		u32 mask, int shift) +{ +	u32 val = read_reg(vpdma, offset); + +	val &= ~(mask << shift); +	val |= (field & mask) << shift; + +	write_reg(vpdma, offset, val); +} + +void vpdma_dump_regs(struct vpdma_data *vpdma) +{ +	struct device *dev = &vpdma->pdev->dev; + +#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r)) + +	dev_dbg(dev, "VPDMA Registers:\n"); + +	DUMPREG(PID); +	DUMPREG(LIST_ADDR); +	DUMPREG(LIST_ATTR); +	DUMPREG(LIST_STAT_SYNC); +	DUMPREG(BG_RGB); +	DUMPREG(BG_YUV); +	DUMPREG(SETUP); +	DUMPREG(MAX_SIZE1); +	DUMPREG(MAX_SIZE2); +	DUMPREG(MAX_SIZE3); + +	/* +	 * dumping registers of only group0 and group3, because VPE channels +	 * lie within group0 and group3 registers +	 */ +	DUMPREG(INT_CHAN_STAT(0)); +	DUMPREG(INT_CHAN_MASK(0)); +	DUMPREG(INT_CHAN_STAT(3)); +	DUMPREG(INT_CHAN_MASK(3)); +	DUMPREG(INT_CLIENT0_STAT); +	DUMPREG(INT_CLIENT0_MASK); +	DUMPREG(INT_CLIENT1_STAT); +	DUMPREG(INT_CLIENT1_MASK); +	DUMPREG(INT_LIST0_STAT); +	DUMPREG(INT_LIST0_MASK); + +	/* +	 * these are registers specific to VPE clients, we can make this +	 * function dump client registers specific to VPE or VIP based on +	 * who is using it +	 */ +	DUMPREG(DEI_CHROMA1_CSTAT); +	DUMPREG(DEI_LUMA1_CSTAT); +	DUMPREG(DEI_CHROMA2_CSTAT); +	DUMPREG(DEI_LUMA2_CSTAT); +	DUMPREG(DEI_CHROMA3_CSTAT); +	DUMPREG(DEI_LUMA3_CSTAT); +	DUMPREG(DEI_MV_IN_CSTAT); +	DUMPREG(DEI_MV_OUT_CSTAT); +	DUMPREG(VIP_UP_Y_CSTAT); +	DUMPREG(VIP_UP_UV_CSTAT); +	DUMPREG(VPI_CTL_CSTAT); +} + +/* + * Allocate a DMA buffer + */ +int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size) +{ +	buf->size = size; +	buf->mapped = false; +	buf->addr = kzalloc(size, GFP_KERNEL); +	if (!buf->addr) +		return -ENOMEM; + +	WARN_ON((u32) buf->addr & VPDMA_DESC_ALIGN); + +	return 0; +} + +void vpdma_free_desc_buf(struct vpdma_buf *buf) +{ +	WARN_ON(buf->mapped); +	kfree(buf->addr); +	buf->addr = NULL; +	buf->size = 0; +} + +/* + * map descriptor/payload DMA buffer, enabling DMA access + */ +int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) +{ +	struct device *dev = &vpdma->pdev->dev; + +	WARN_ON(buf->mapped); +	buf->dma_addr = dma_map_single(dev, buf->addr, buf->size, +				DMA_TO_DEVICE); +	if (dma_mapping_error(dev, buf->dma_addr)) { +		dev_err(dev, "failed to map buffer\n"); +		return -EINVAL; +	} + +	buf->mapped = true; + +	return 0; +} + +/* + * unmap descriptor/payload DMA buffer, disabling DMA access and + * allowing the main processor to acces the data + */ +void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf) +{ +	struct device *dev = &vpdma->pdev->dev; + +	if (buf->mapped) +		dma_unmap_single(dev, buf->dma_addr, buf->size, DMA_TO_DEVICE); + +	buf->mapped = false; +} + +/* + * create a descriptor list, the user of this list will append configuration, + * control and data descriptors to this list, this list will be submitted to + * VPDMA. VPDMA's list parser will go through each descriptor and perform the + * required DMA operations + */ +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type) +{ +	int r; + +	r = vpdma_alloc_desc_buf(&list->buf, size); +	if (r) +		return r; + +	list->next = list->buf.addr; + +	list->type = type; + +	return 0; +} + +/* + * once a descriptor list is parsed by VPDMA, we reset the list by emptying it, + * to allow new descriptors to be added to the list. + */ +void vpdma_reset_desc_list(struct vpdma_desc_list *list) +{ +	list->next = list->buf.addr; +} + +/* + * free the buffer allocated fot the VPDMA descriptor list, this should be + * called when the user doesn't want to use VPDMA any more. + */ +void vpdma_free_desc_list(struct vpdma_desc_list *list) +{ +	vpdma_free_desc_buf(&list->buf); + +	list->next = NULL; +} + +static bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num) +{ +	return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16); +} + +/* + * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion + */ +int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list) +{ +	/* we always use the first list */ +	int list_num = 0; +	int list_size; + +	if (vpdma_list_busy(vpdma, list_num)) +		return -EBUSY; + +	/* 16-byte granularity */ +	list_size = (list->next - list->buf.addr) >> 4; + +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr); + +	write_reg(vpdma, VPDMA_LIST_ATTR, +			(list_num << VPDMA_LIST_NUM_SHFT) | +			(list->type << VPDMA_LIST_TYPE_SHFT) | +			list_size); + +	return 0; +} + +static void dump_cfd(struct vpdma_cfd *cfd) +{ +	int class; + +	class = cfd_get_class(cfd); + +	pr_debug("config descriptor of payload class: %s\n", +		class == CFD_CLS_BLOCK ? "simple block" : +		"address data block"); + +	if (class == CFD_CLS_BLOCK) +		pr_debug("word0: dst_addr_offset = 0x%08x\n", +			cfd->dest_addr_offset); + +	if (class == CFD_CLS_BLOCK) +		pr_debug("word1: num_data_wrds = %d\n", cfd->block_len); + +	pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr); + +	pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, " +		"payload_len = %d\n", cfd_get_pkt_type(cfd), +		cfd_get_direct(cfd), class, cfd_get_dest(cfd), +		cfd_get_payload_len(cfd)); +} + +/* + * append a configuration descriptor to the given descriptor list, where the + * payload is in the form of a simple data block specified in the descriptor + * header, this is used to upload scaler coefficients to the scaler module + */ +void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, +		struct vpdma_buf *blk, u32 dest_offset) +{ +	struct vpdma_cfd *cfd; +	int len = blk->size; + +	WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN); + +	cfd = list->next; +	WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); + +	cfd->dest_addr_offset = dest_offset; +	cfd->block_len = len; +	cfd->payload_addr = (u32) blk->dma_addr; +	cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK, +				client, len >> 4); + +	list->next = cfd + 1; + +	dump_cfd(cfd); +} + +/* + * append a configuration descriptor to the given descriptor list, where the + * payload is in the address data block format, this is used to a configure a + * discontiguous set of MMRs + */ +void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, +		struct vpdma_buf *adb) +{ +	struct vpdma_cfd *cfd; +	unsigned int len = adb->size; + +	WARN_ON(len & VPDMA_ADB_SIZE_ALIGN); +	WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN); + +	cfd = list->next; +	BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size)); + +	cfd->w0 = 0; +	cfd->w1 = 0; +	cfd->payload_addr = (u32) adb->dma_addr; +	cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB, +				client, len >> 4); + +	list->next = cfd + 1; + +	dump_cfd(cfd); +}; + +/* + * control descriptor format change based on what type of control descriptor it + * is, we only use 'sync on channel' control descriptors for now, so assume it's + * that + */ +static void dump_ctd(struct vpdma_ctd *ctd) +{ +	pr_debug("control descriptor\n"); + +	pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n", +		ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd)); +} + +/* + * append a 'sync on channel' type control descriptor to the given descriptor + * list, this descriptor stalls the VPDMA list till the time DMA is completed + * on the specified channel + */ +void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, +		enum vpdma_channel chan) +{ +	struct vpdma_ctd *ctd; + +	ctd = list->next; +	WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size)); + +	ctd->w0 = 0; +	ctd->w1 = 0; +	ctd->w2 = 0; +	ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num, +				CTD_TYPE_SYNC_ON_CHANNEL); + +	list->next = ctd + 1; + +	dump_ctd(ctd); +} + +static void dump_dtd(struct vpdma_dtd *dtd) +{ +	int dir, chan; + +	dir = dtd_get_dir(dtd); +	chan = dtd_get_chan(dtd); + +	pr_debug("%s data transfer descriptor for channel %d\n", +		dir == DTD_DIR_OUT ? "outbound" : "inbound", chan); + +	pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, " +		"even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n", +		dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd), +		dtd_get_1d(dtd), dtd_get_even_line_skip(dtd), +		dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd)); + +	if (dir == DTD_DIR_IN) +		pr_debug("word1: line_length = %d, xfer_height = %d\n", +			dtd_get_line_length(dtd), dtd_get_xfer_height(dtd)); + +	pr_debug("word2: start_addr = 0x%08x\n", dtd->start_addr); + +	pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, " +		"pri = %d, next_chan = %d\n", dtd_get_pkt_type(dtd), +		dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd), +		dtd_get_next_chan(dtd)); + +	if (dir == DTD_DIR_IN) +		pr_debug("word4: frame_width = %d, frame_height = %d\n", +			dtd_get_frame_width(dtd), dtd_get_frame_height(dtd)); +	else +		pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, " +			"drp_data = %d, use_desc_reg = %d\n", +			dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd), +			dtd_get_drop_data(dtd), dtd_get_use_desc(dtd)); + +	if (dir == DTD_DIR_IN) +		pr_debug("word5: hor_start = %d, ver_start = %d\n", +			dtd_get_h_start(dtd), dtd_get_v_start(dtd)); +	else +		pr_debug("word5: max_width %d, max_height %d\n", +			dtd_get_max_width(dtd), dtd_get_max_height(dtd)); + +	pr_debug("word6: client specific attr0 = 0x%08x\n", dtd->client_attr0); +	pr_debug("word7: client specific attr1 = 0x%08x\n", dtd->client_attr1); +} + +/* + * append an outbound data transfer descriptor to the given descriptor list, + * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this decriptor + * @width: width of the image in pixels in memory + * @c_rect: compose params of output image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * chan: VPDMA channel + * flags: VPDMA flags to configure some descriptor fileds + */ +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, +		const struct v4l2_rect *c_rect, +		const struct vpdma_data_format *fmt, dma_addr_t dma_addr, +		enum vpdma_channel chan, u32 flags) +{ +	int priority = 0; +	int field = 0; +	int notify = 1; +	int channel, next_chan; +	struct v4l2_rect rect = *c_rect; +	int depth = fmt->depth; +	int stride; +	struct vpdma_dtd *dtd; + +	channel = next_chan = chan_info[chan].num; + +	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && +			fmt->data_type == DATA_TYPE_C420) { +		rect.height >>= 1; +		rect.top >>= 1; +		depth = 8; +	} + +	stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN); + +	dma_addr += rect.top * stride + (rect.left * depth >> 3); + +	dtd = list->next; +	WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); + +	dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, +					notify, +					field, +					!!(flags & VPDMA_DATA_FRAME_1D), +					!!(flags & VPDMA_DATA_EVEN_LINE_SKIP), +					!!(flags & VPDMA_DATA_ODD_LINE_SKIP), +					stride); +	dtd->w1 = 0; +	dtd->start_addr = (u32) dma_addr; +	dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), +				DTD_DIR_OUT, channel, priority, next_chan); +	dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0); +	dtd->max_width_height = dtd_max_width_height(MAX_OUT_WIDTH_1920, +					MAX_OUT_HEIGHT_1080); +	dtd->client_attr0 = 0; +	dtd->client_attr1 = 0; + +	list->next = dtd + 1; + +	dump_dtd(dtd); +} + +/* + * append an inbound data transfer descriptor to the given descriptor list, + * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel + * + * @list: vpdma desc list to which we add this decriptor + * @width: width of the image in pixels in memory(not the cropped width) + * @c_rect: crop params of input image + * @fmt: vpdma data format of the buffer + * dma_addr: dma address as seen by VPDMA + * chan: VPDMA channel + * field: top or bottom field info of the input image + * flags: VPDMA flags to configure some descriptor fileds + * frame_width/height: the complete width/height of the image presented to the + *			client (this makes sense when multiple channels are + *			connected to the same client, forming a larger frame) + * start_h, start_v: position where the given channel starts providing pixel + *			data to the client (makes sense when multiple channels + *			contribute to the client) + */ +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, +		const struct v4l2_rect *c_rect, +		const struct vpdma_data_format *fmt, dma_addr_t dma_addr, +		enum vpdma_channel chan, int field, u32 flags, int frame_width, +		int frame_height, int start_h, int start_v) +{ +	int priority = 0; +	int notify = 1; +	int depth = fmt->depth; +	int channel, next_chan; +	struct v4l2_rect rect = *c_rect; +	int stride; +	struct vpdma_dtd *dtd; + +	channel = next_chan = chan_info[chan].num; + +	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV && +			fmt->data_type == DATA_TYPE_C420) { +		rect.height >>= 1; +		rect.top >>= 1; +		depth = 8; +	} + +	stride = ALIGN((depth * width) >> 3, VPDMA_STRIDE_ALIGN); + +	dma_addr += rect.top * stride + (rect.left * depth >> 3); + +	dtd = list->next; +	WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size)); + +	dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type, +					notify, +					field, +					!!(flags & VPDMA_DATA_FRAME_1D), +					!!(flags & VPDMA_DATA_EVEN_LINE_SKIP), +					!!(flags & VPDMA_DATA_ODD_LINE_SKIP), +					stride); + +	dtd->xfer_length_height = dtd_xfer_length_height(rect.width, +					rect.height); +	dtd->start_addr = (u32) dma_addr; +	dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED), +				DTD_DIR_IN, channel, priority, next_chan); +	dtd->frame_width_height = dtd_frame_width_height(frame_width, +					frame_height); +	dtd->start_h_v = dtd_start_h_v(start_h, start_v); +	dtd->client_attr0 = 0; +	dtd->client_attr1 = 0; + +	list->next = dtd + 1; + +	dump_dtd(dtd); +} + +/* set or clear the mask for list complete interrupt */ +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num, +		bool enable) +{ +	u32 val; + +	val = read_reg(vpdma, VPDMA_INT_LIST0_MASK); +	if (enable) +		val |= (1 << (list_num * 2)); +	else +		val &= ~(1 << (list_num * 2)); +	write_reg(vpdma, VPDMA_INT_LIST0_MASK, val); +} + +/* clear previosuly occured list intterupts in the LIST_STAT register */ +void vpdma_clear_list_stat(struct vpdma_data *vpdma) +{ +	write_reg(vpdma, VPDMA_INT_LIST0_STAT, +		read_reg(vpdma, VPDMA_INT_LIST0_STAT)); +} + +/* + * configures the output mode of the line buffer for the given client, the + * line buffer content can either be mirrored(each line repeated twice) or + * passed to the client as is + */ +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, +		enum vpdma_channel chan) +{ +	int client_cstat = chan_info[chan].cstat_offset; + +	write_field_reg(vpdma, client_cstat, line_mode, +		VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT); +} + +/* + * configures the event which should trigger VPDMA transfer for the given + * client + */ +void vpdma_set_frame_start_event(struct vpdma_data *vpdma, +		enum vpdma_frame_start_event fs_event, +		enum vpdma_channel chan) +{ +	int client_cstat = chan_info[chan].cstat_offset; + +	write_field_reg(vpdma, client_cstat, fs_event, +		VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT); +} + +static void vpdma_firmware_cb(const struct firmware *f, void *context) +{ +	struct vpdma_data *vpdma = context; +	struct vpdma_buf fw_dma_buf; +	int i, r; + +	dev_dbg(&vpdma->pdev->dev, "firmware callback\n"); + +	if (!f || !f->data) { +		dev_err(&vpdma->pdev->dev, "couldn't get firmware\n"); +		return; +	} + +	/* already initialized */ +	if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, +			VPDMA_LIST_RDY_SHFT)) { +		vpdma->cb(vpdma->pdev); +		return; +	} + +	r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size); +	if (r) { +		dev_err(&vpdma->pdev->dev, +			"failed to allocate dma buffer for firmware\n"); +		goto rel_fw; +	} + +	memcpy(fw_dma_buf.addr, f->data, f->size); + +	vpdma_map_desc_buf(vpdma, &fw_dma_buf); + +	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr); + +	for (i = 0; i < 100; i++) {		/* max 1 second */ +		msleep_interruptible(10); + +		if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK, +				VPDMA_LIST_RDY_SHFT)) +			break; +	} + +	if (i == 100) { +		dev_err(&vpdma->pdev->dev, "firmware upload failed\n"); +		goto free_buf; +	} + +	vpdma->cb(vpdma->pdev); + +free_buf: +	vpdma_unmap_desc_buf(vpdma, &fw_dma_buf); + +	vpdma_free_desc_buf(&fw_dma_buf); +rel_fw: +	release_firmware(f); +} + +static int vpdma_load_firmware(struct vpdma_data *vpdma) +{ +	int r; +	struct device *dev = &vpdma->pdev->dev; + +	r = request_firmware_nowait(THIS_MODULE, 1, +		(const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma, +		vpdma_firmware_cb); +	if (r) { +		dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE); +		return r; +	} else { +		dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE); +	} + +	return 0; +} + +struct vpdma_data *vpdma_create(struct platform_device *pdev, +		void (*cb)(struct platform_device *pdev)) +{ +	struct resource *res; +	struct vpdma_data *vpdma; +	int r; + +	dev_dbg(&pdev->dev, "vpdma_create\n"); + +	vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL); +	if (!vpdma) { +		dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n"); +		return ERR_PTR(-ENOMEM); +	} + +	vpdma->pdev = pdev; +	vpdma->cb = cb; + +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma"); +	if (res == NULL) { +		dev_err(&pdev->dev, "missing platform resources data\n"); +		return ERR_PTR(-ENODEV); +	} + +	vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); +	if (!vpdma->base) { +		dev_err(&pdev->dev, "failed to ioremap\n"); +		return ERR_PTR(-ENOMEM); +	} + +	r = vpdma_load_firmware(vpdma); +	if (r) { +		pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE); +		return ERR_PTR(r); +	} + +	return vpdma; +} +MODULE_FIRMWARE(VPDMA_FIRMWARE); diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h new file mode 100644 index 00000000000..2bd8fb05038 --- /dev/null +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#ifndef __TI_VPDMA_H_ +#define __TI_VPDMA_H_ + +/* + * A vpdma_buf tracks the size, DMA address and mapping status of each + * driver DMA area. + */ +struct vpdma_buf { +	void			*addr; +	dma_addr_t		dma_addr; +	size_t			size; +	bool			mapped; +}; + +struct vpdma_desc_list { +	struct vpdma_buf buf; +	void *next; +	int type; +}; + +struct vpdma_data { +	void __iomem		*base; + +	struct platform_device	*pdev; + +	/* callback to VPE driver when the firmware is loaded */ +	void (*cb)(struct platform_device *pdev); +}; + +enum vpdma_data_format_type { +	VPDMA_DATA_FMT_TYPE_YUV, +	VPDMA_DATA_FMT_TYPE_RGB, +	VPDMA_DATA_FMT_TYPE_MISC, +}; + +struct vpdma_data_format { +	enum vpdma_data_format_type type; +	int data_type; +	u8 depth; +}; + +#define VPDMA_DESC_ALIGN		16	/* 16-byte descriptor alignment */ +#define VPDMA_STRIDE_ALIGN		16	/* +						 * line stride of source and dest +						 * buffers should be 16 byte aligned +						 */ +#define VPDMA_DTD_DESC_SIZE		32	/* 8 words */ +#define VPDMA_CFD_CTD_DESC_SIZE		16	/* 4 words */ + +#define VPDMA_LIST_TYPE_NORMAL		0 +#define VPDMA_LIST_TYPE_SELF_MODIFYING	1 +#define VPDMA_LIST_TYPE_DOORBELL	2 + +enum vpdma_yuv_formats { +	VPDMA_DATA_FMT_Y444 = 0, +	VPDMA_DATA_FMT_Y422, +	VPDMA_DATA_FMT_Y420, +	VPDMA_DATA_FMT_C444, +	VPDMA_DATA_FMT_C422, +	VPDMA_DATA_FMT_C420, +	VPDMA_DATA_FMT_YC422, +	VPDMA_DATA_FMT_YC444, +	VPDMA_DATA_FMT_CY422, +}; + +enum vpdma_rgb_formats { +	VPDMA_DATA_FMT_RGB565 = 0, +	VPDMA_DATA_FMT_ARGB16_1555, +	VPDMA_DATA_FMT_ARGB16, +	VPDMA_DATA_FMT_RGBA16_5551, +	VPDMA_DATA_FMT_RGBA16, +	VPDMA_DATA_FMT_ARGB24, +	VPDMA_DATA_FMT_RGB24, +	VPDMA_DATA_FMT_ARGB32, +	VPDMA_DATA_FMT_RGBA24, +	VPDMA_DATA_FMT_RGBA32, +	VPDMA_DATA_FMT_BGR565, +	VPDMA_DATA_FMT_ABGR16_1555, +	VPDMA_DATA_FMT_ABGR16, +	VPDMA_DATA_FMT_BGRA16_5551, +	VPDMA_DATA_FMT_BGRA16, +	VPDMA_DATA_FMT_ABGR24, +	VPDMA_DATA_FMT_BGR24, +	VPDMA_DATA_FMT_ABGR32, +	VPDMA_DATA_FMT_BGRA24, +	VPDMA_DATA_FMT_BGRA32, +}; + +enum vpdma_misc_formats { +	VPDMA_DATA_FMT_MV = 0, +}; + +extern const struct vpdma_data_format vpdma_yuv_fmts[]; +extern const struct vpdma_data_format vpdma_rgb_fmts[]; +extern const struct vpdma_data_format vpdma_misc_fmts[]; + +enum vpdma_frame_start_event { +	VPDMA_FSEVENT_HDMI_FID = 0, +	VPDMA_FSEVENT_DVO2_FID, +	VPDMA_FSEVENT_HDCOMP_FID, +	VPDMA_FSEVENT_SD_FID, +	VPDMA_FSEVENT_LM_FID0, +	VPDMA_FSEVENT_LM_FID1, +	VPDMA_FSEVENT_LM_FID2, +	VPDMA_FSEVENT_CHANNEL_ACTIVE, +}; + +/* + * VPDMA channel numbers + */ +enum vpdma_channel { +	VPE_CHAN_LUMA1_IN, +	VPE_CHAN_CHROMA1_IN, +	VPE_CHAN_LUMA2_IN, +	VPE_CHAN_CHROMA2_IN, +	VPE_CHAN_LUMA3_IN, +	VPE_CHAN_CHROMA3_IN, +	VPE_CHAN_MV_IN, +	VPE_CHAN_MV_OUT, +	VPE_CHAN_LUMA_OUT, +	VPE_CHAN_CHROMA_OUT, +	VPE_CHAN_RGB_OUT, +}; + +/* flags for VPDMA data descriptors */ +#define VPDMA_DATA_ODD_LINE_SKIP	(1 << 0) +#define VPDMA_DATA_EVEN_LINE_SKIP	(1 << 1) +#define VPDMA_DATA_FRAME_1D		(1 << 2) +#define VPDMA_DATA_MODE_TILED		(1 << 3) + +/* + * client identifiers used for configuration descriptors + */ +#define CFD_MMR_CLIENT		0 +#define CFD_SC_CLIENT		4 + +/* Address data block header format */ +struct vpdma_adb_hdr { +	u32			offset; +	u32			nwords; +	u32			reserved0; +	u32			reserved1; +}; + +/* helpers for creating ADB headers for config descriptors MMRs as client */ +#define ADB_ADDR(dma_buf, str, fld)	((dma_buf)->addr + offsetof(str, fld)) +#define MMR_ADB_ADDR(buf, str, fld)	ADB_ADDR(&(buf), struct str, fld) + +#define VPDMA_SET_MMR_ADB_HDR(buf, str, hdr, regs, offset_a)	\ +	do {							\ +		struct vpdma_adb_hdr *h;			\ +		struct str *adb = NULL;				\ +		h = MMR_ADB_ADDR(buf, str, hdr);		\ +		h->offset = (offset_a);				\ +		h->nwords = sizeof(adb->regs) >> 2;		\ +	} while (0) + +/* vpdma descriptor buffer allocation and management */ +int vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size); +void vpdma_free_desc_buf(struct vpdma_buf *buf); +int vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); +void vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf); + +/* vpdma descriptor list funcs */ +int vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type); +void vpdma_reset_desc_list(struct vpdma_desc_list *list); +void vpdma_free_desc_list(struct vpdma_desc_list *list); +int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list); + +/* helpers for creating vpdma descriptors */ +void vpdma_add_cfd_block(struct vpdma_desc_list *list, int client, +		struct vpdma_buf *blk, u32 dest_offset); +void vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client, +		struct vpdma_buf *adb); +void vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list, +		enum vpdma_channel chan); +void vpdma_add_out_dtd(struct vpdma_desc_list *list, int width, +		const struct v4l2_rect *c_rect, +		const struct vpdma_data_format *fmt, dma_addr_t dma_addr, +		enum vpdma_channel chan, u32 flags); +void vpdma_add_in_dtd(struct vpdma_desc_list *list, int width, +		const struct v4l2_rect *c_rect, +		const struct vpdma_data_format *fmt, dma_addr_t dma_addr, +		enum vpdma_channel chan, int field, u32 flags, int frame_width, +		int frame_height, int start_h, int start_v); + +/* vpdma list interrupt management */ +void vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int list_num, +		bool enable); +void vpdma_clear_list_stat(struct vpdma_data *vpdma); + +/* vpdma client configuration */ +void vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode, +		enum vpdma_channel chan); +void vpdma_set_frame_start_event(struct vpdma_data *vpdma, +		enum vpdma_frame_start_event fs_event, enum vpdma_channel chan); + +void vpdma_dump_regs(struct vpdma_data *vpdma); + +/* initialize vpdma, passed with VPE's platform device pointer */ +struct vpdma_data *vpdma_create(struct platform_device *pdev, +		void (*cb)(struct platform_device *pdev)); + +#endif diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti-vpe/vpdma_priv.h new file mode 100644 index 00000000000..c1a6ce1884f --- /dev/null +++ b/drivers/media/platform/ti-vpe/vpdma_priv.h @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#ifndef _TI_VPDMA_PRIV_H_ +#define _TI_VPDMA_PRIV_H_ + +/* + * VPDMA Register offsets + */ + +/* Top level */ +#define VPDMA_PID		0x00 +#define VPDMA_LIST_ADDR		0x04 +#define VPDMA_LIST_ATTR		0x08 +#define VPDMA_LIST_STAT_SYNC	0x0c +#define VPDMA_BG_RGB		0x18 +#define VPDMA_BG_YUV		0x1c +#define VPDMA_SETUP		0x30 +#define VPDMA_MAX_SIZE1		0x34 +#define VPDMA_MAX_SIZE2		0x38 +#define VPDMA_MAX_SIZE3		0x3c + +/* Interrupts */ +#define VPDMA_INT_CHAN_STAT(grp)	(0x40 + grp * 8) +#define VPDMA_INT_CHAN_MASK(grp)	(VPDMA_INT_CHAN_STAT(grp) + 4) +#define VPDMA_INT_CLIENT0_STAT		0x78 +#define VPDMA_INT_CLIENT0_MASK		0x7c +#define VPDMA_INT_CLIENT1_STAT		0x80 +#define VPDMA_INT_CLIENT1_MASK		0x84 +#define VPDMA_INT_LIST0_STAT		0x88 +#define VPDMA_INT_LIST0_MASK		0x8c + +#define VPDMA_PERFMON(i)		(0x200 + i * 4) + +/* VPE specific client registers */ +#define VPDMA_DEI_CHROMA1_CSTAT		0x0300 +#define VPDMA_DEI_LUMA1_CSTAT		0x0304 +#define VPDMA_DEI_LUMA2_CSTAT		0x0308 +#define VPDMA_DEI_CHROMA2_CSTAT		0x030c +#define VPDMA_DEI_LUMA3_CSTAT		0x0310 +#define VPDMA_DEI_CHROMA3_CSTAT		0x0314 +#define VPDMA_DEI_MV_IN_CSTAT		0x0330 +#define VPDMA_DEI_MV_OUT_CSTAT		0x033c +#define VPDMA_VIP_UP_Y_CSTAT		0x0390 +#define VPDMA_VIP_UP_UV_CSTAT		0x0394 +#define VPDMA_VPI_CTL_CSTAT		0x03d0 + +/* Reg field info for VPDMA_CLIENT_CSTAT registers */ +#define VPDMA_CSTAT_LINE_MODE_MASK	0x03 +#define VPDMA_CSTAT_LINE_MODE_SHIFT	8 +#define VPDMA_CSTAT_FRAME_START_MASK	0xf +#define VPDMA_CSTAT_FRAME_START_SHIFT	10 + +#define VPDMA_LIST_NUM_MASK		0x07 +#define VPDMA_LIST_NUM_SHFT		24 +#define VPDMA_LIST_STOP_SHFT		20 +#define VPDMA_LIST_RDY_MASK		0x01 +#define VPDMA_LIST_RDY_SHFT		19 +#define VPDMA_LIST_TYPE_MASK		0x03 +#define VPDMA_LIST_TYPE_SHFT		16 +#define VPDMA_LIST_SIZE_MASK		0xffff + +/* VPDMA data type values for data formats */ +#define DATA_TYPE_Y444				0x0 +#define DATA_TYPE_Y422				0x1 +#define DATA_TYPE_Y420				0x2 +#define DATA_TYPE_C444				0x4 +#define DATA_TYPE_C422				0x5 +#define DATA_TYPE_C420				0x6 +#define DATA_TYPE_YC422				0x7 +#define DATA_TYPE_YC444				0x8 +#define DATA_TYPE_CY422				0x27 + +#define DATA_TYPE_RGB16_565			0x0 +#define DATA_TYPE_ARGB_1555			0x1 +#define DATA_TYPE_ARGB_4444			0x2 +#define DATA_TYPE_RGBA_5551			0x3 +#define DATA_TYPE_RGBA_4444			0x4 +#define DATA_TYPE_ARGB24_6666			0x5 +#define DATA_TYPE_RGB24_888			0x6 +#define DATA_TYPE_ARGB32_8888			0x7 +#define DATA_TYPE_RGBA24_6666			0x8 +#define DATA_TYPE_RGBA32_8888			0x9 +#define DATA_TYPE_BGR16_565			0x10 +#define DATA_TYPE_ABGR_1555			0x11 +#define DATA_TYPE_ABGR_4444			0x12 +#define DATA_TYPE_BGRA_5551			0x13 +#define DATA_TYPE_BGRA_4444			0x14 +#define DATA_TYPE_ABGR24_6666			0x15 +#define DATA_TYPE_BGR24_888			0x16 +#define DATA_TYPE_ABGR32_8888			0x17 +#define DATA_TYPE_BGRA24_6666			0x18 +#define DATA_TYPE_BGRA32_8888			0x19 + +#define DATA_TYPE_MV				0x3 + +/* VPDMA channel numbers(only VPE channels for now) */ +#define	VPE_CHAN_NUM_LUMA1_IN		0 +#define	VPE_CHAN_NUM_CHROMA1_IN		1 +#define	VPE_CHAN_NUM_LUMA2_IN		2 +#define	VPE_CHAN_NUM_CHROMA2_IN		3 +#define	VPE_CHAN_NUM_LUMA3_IN		4 +#define	VPE_CHAN_NUM_CHROMA3_IN		5 +#define	VPE_CHAN_NUM_MV_IN		12 +#define	VPE_CHAN_NUM_MV_OUT		15 +#define	VPE_CHAN_NUM_LUMA_OUT		102 +#define	VPE_CHAN_NUM_CHROMA_OUT		103 +#define	VPE_CHAN_NUM_RGB_OUT		106 + +/* + * a VPDMA address data block payload for a configuration descriptor needs to + * have each sub block length as a multiple of 16 bytes. Therefore, the overall + * size of the payload also needs to be a multiple of 16 bytes. The sub block + * lengths should be ensured to be aligned by the VPDMA user. + */ +#define VPDMA_ADB_SIZE_ALIGN		0x0f + +/* + * data transfer descriptor + */ +struct vpdma_dtd { +	u32			type_ctl_stride; +	union { +		u32		xfer_length_height; +		u32		w1; +	}; +	dma_addr_t		start_addr; +	u32			pkt_ctl; +	union { +		u32		frame_width_height;	/* inbound */ +		dma_addr_t	desc_write_addr;	/* outbound */ +	}; +	union { +		u32		start_h_v;		/* inbound */ +		u32		max_width_height;	/* outbound */ +	}; +	u32			client_attr0; +	u32			client_attr1; +}; + +/* Data Transfer Descriptor specifics */ +#define DTD_NO_NOTIFY		0 +#define DTD_NOTIFY		1 + +#define DTD_PKT_TYPE		0xa +#define DTD_DIR_IN		0 +#define DTD_DIR_OUT		1 + +/* type_ctl_stride */ +#define DTD_DATA_TYPE_MASK	0x3f +#define DTD_DATA_TYPE_SHFT	26 +#define DTD_NOTIFY_MASK		0x01 +#define DTD_NOTIFY_SHFT		25 +#define DTD_FIELD_MASK		0x01 +#define DTD_FIELD_SHFT		24 +#define DTD_1D_MASK		0x01 +#define DTD_1D_SHFT		23 +#define DTD_EVEN_LINE_SKIP_MASK	0x01 +#define DTD_EVEN_LINE_SKIP_SHFT	20 +#define DTD_ODD_LINE_SKIP_MASK	0x01 +#define DTD_ODD_LINE_SKIP_SHFT	16 +#define DTD_LINE_STRIDE_MASK	0xffff +#define DTD_LINE_STRIDE_SHFT	0 + +/* xfer_length_height */ +#define DTD_LINE_LENGTH_MASK	0xffff +#define DTD_LINE_LENGTH_SHFT	16 +#define DTD_XFER_HEIGHT_MASK	0xffff +#define DTD_XFER_HEIGHT_SHFT	0 + +/* pkt_ctl */ +#define DTD_PKT_TYPE_MASK	0x1f +#define DTD_PKT_TYPE_SHFT	27 +#define DTD_MODE_MASK		0x01 +#define DTD_MODE_SHFT		26 +#define DTD_DIR_MASK		0x01 +#define DTD_DIR_SHFT		25 +#define DTD_CHAN_MASK		0x01ff +#define DTD_CHAN_SHFT		16 +#define DTD_PRI_MASK		0x0f +#define DTD_PRI_SHFT		9 +#define DTD_NEXT_CHAN_MASK	0x01ff +#define DTD_NEXT_CHAN_SHFT	0 + +/* frame_width_height */ +#define DTD_FRAME_WIDTH_MASK	0xffff +#define DTD_FRAME_WIDTH_SHFT	16 +#define DTD_FRAME_HEIGHT_MASK	0xffff +#define DTD_FRAME_HEIGHT_SHFT	0 + +/* start_h_v */ +#define DTD_H_START_MASK	0xffff +#define DTD_H_START_SHFT	16 +#define DTD_V_START_MASK	0xffff +#define DTD_V_START_SHFT	0 + +#define DTD_DESC_START_SHIFT	5 +#define DTD_WRITE_DESC_MASK	0x01 +#define DTD_WRITE_DESC_SHIFT	2 +#define DTD_DROP_DATA_MASK	0x01 +#define DTD_DROP_DATA_SHIFT	1 +#define DTD_USE_DESC_MASK	0x01 +#define DTD_USE_DESC_SHIFT	0 + +/* max_width_height */ +#define DTD_MAX_WIDTH_MASK	0x07 +#define DTD_MAX_WIDTH_SHFT	4 +#define DTD_MAX_HEIGHT_MASK	0x07 +#define DTD_MAX_HEIGHT_SHFT	0 + +/* max width configurations */ + /* unlimited width */ +#define	MAX_OUT_WIDTH_UNLIMITED		0 +/* as specified in max_size1 reg */ +#define MAX_OUT_WIDTH_REG1		1 +/* as specified in max_size2 reg */ +#define MAX_OUT_WIDTH_REG2		2 +/* as specified in max_size3 reg */ +#define	MAX_OUT_WIDTH_REG3		3 +/* maximum of 352 pixels as width */ +#define MAX_OUT_WIDTH_352		4 +/* maximum of 768 pixels as width */ +#define	MAX_OUT_WIDTH_768		5 +/* maximum of 1280 pixels width */ +#define	MAX_OUT_WIDTH_1280		6 +/* maximum of 1920 pixels as width */ +#define	MAX_OUT_WIDTH_1920		7 + +/* max height configurations */ + /* unlimited height */ +#define	MAX_OUT_HEIGHT_UNLIMITED	0 +/* as specified in max_size1 reg */ +#define MAX_OUT_HEIGHT_REG1		1 +/* as specified in max_size2 reg */ +#define MAX_OUT_HEIGHT_REG2		2 +/* as specified in max_size3 reg */ +#define	MAX_OUT_HEIGHT_REG3		3 +/* maximum of 288 lines as height */ +#define MAX_OUT_HEIGHT_288		4 +/* maximum of 576 lines as height */ +#define	MAX_OUT_HEIGHT_576		5 +/* maximum of 720 lines as height */ +#define	MAX_OUT_HEIGHT_720		6 +/* maximum of 1080 lines as height */ +#define	MAX_OUT_HEIGHT_1080		7 + +static inline u32 dtd_type_ctl_stride(int type, bool notify, int field, +			bool one_d, bool even_line_skip, bool odd_line_skip, +			int line_stride) +{ +	return (type << DTD_DATA_TYPE_SHFT) | (notify << DTD_NOTIFY_SHFT) | +		(field << DTD_FIELD_SHFT) | (one_d << DTD_1D_SHFT) | +		(even_line_skip << DTD_EVEN_LINE_SKIP_SHFT) | +		(odd_line_skip << DTD_ODD_LINE_SKIP_SHFT) | +		line_stride; +} + +static inline u32 dtd_xfer_length_height(int line_length, int xfer_height) +{ +	return (line_length << DTD_LINE_LENGTH_SHFT) | xfer_height; +} + +static inline u32 dtd_pkt_ctl(bool mode, bool dir, int chan, int pri, +			int next_chan) +{ +	return (DTD_PKT_TYPE << DTD_PKT_TYPE_SHFT) | (mode << DTD_MODE_SHFT) | +		(dir << DTD_DIR_SHFT) | (chan << DTD_CHAN_SHFT) | +		(pri << DTD_PRI_SHFT) | next_chan; +} + +static inline u32 dtd_frame_width_height(int width, int height) +{ +	return (width << DTD_FRAME_WIDTH_SHFT) | height; +} + +static inline u32 dtd_desc_write_addr(unsigned int addr, bool write_desc, +			bool drop_data, bool use_desc) +{ +	return (addr << DTD_DESC_START_SHIFT) | +		(write_desc << DTD_WRITE_DESC_SHIFT) | +		(drop_data << DTD_DROP_DATA_SHIFT) | +		use_desc; +} + +static inline u32 dtd_start_h_v(int h_start, int v_start) +{ +	return (h_start << DTD_H_START_SHFT) | v_start; +} + +static inline u32 dtd_max_width_height(int max_width, int max_height) +{ +	return (max_width << DTD_MAX_WIDTH_SHFT) | max_height; +} + +static inline int dtd_get_data_type(struct vpdma_dtd *dtd) +{ +	return dtd->type_ctl_stride >> DTD_DATA_TYPE_SHFT; +} + +static inline bool dtd_get_notify(struct vpdma_dtd *dtd) +{ +	return (dtd->type_ctl_stride >> DTD_NOTIFY_SHFT) & DTD_NOTIFY_MASK; +} + +static inline int dtd_get_field(struct vpdma_dtd *dtd) +{ +	return (dtd->type_ctl_stride >> DTD_FIELD_SHFT) & DTD_FIELD_MASK; +} + +static inline bool dtd_get_1d(struct vpdma_dtd *dtd) +{ +	return (dtd->type_ctl_stride >> DTD_1D_SHFT) & DTD_1D_MASK; +} + +static inline bool dtd_get_even_line_skip(struct vpdma_dtd *dtd) +{ +	return (dtd->type_ctl_stride >> DTD_EVEN_LINE_SKIP_SHFT) +		& DTD_EVEN_LINE_SKIP_MASK; +} + +static inline bool dtd_get_odd_line_skip(struct vpdma_dtd *dtd) +{ +	return (dtd->type_ctl_stride >> DTD_ODD_LINE_SKIP_SHFT) +		& DTD_ODD_LINE_SKIP_MASK; +} + +static inline int dtd_get_line_stride(struct vpdma_dtd *dtd) +{ +	return dtd->type_ctl_stride & DTD_LINE_STRIDE_MASK; +} + +static inline int dtd_get_line_length(struct vpdma_dtd *dtd) +{ +	return dtd->xfer_length_height >> DTD_LINE_LENGTH_SHFT; +} + +static inline int dtd_get_xfer_height(struct vpdma_dtd *dtd) +{ +	return dtd->xfer_length_height & DTD_XFER_HEIGHT_MASK; +} + +static inline int dtd_get_pkt_type(struct vpdma_dtd *dtd) +{ +	return dtd->pkt_ctl >> DTD_PKT_TYPE_SHFT; +} + +static inline bool dtd_get_mode(struct vpdma_dtd *dtd) +{ +	return (dtd->pkt_ctl >> DTD_MODE_SHFT) & DTD_MODE_MASK; +} + +static inline bool dtd_get_dir(struct vpdma_dtd *dtd) +{ +	return (dtd->pkt_ctl >> DTD_DIR_SHFT) & DTD_DIR_MASK; +} + +static inline int dtd_get_chan(struct vpdma_dtd *dtd) +{ +	return (dtd->pkt_ctl >> DTD_CHAN_SHFT) & DTD_CHAN_MASK; +} + +static inline int dtd_get_priority(struct vpdma_dtd *dtd) +{ +	return (dtd->pkt_ctl >> DTD_PRI_SHFT) & DTD_PRI_MASK; +} + +static inline int dtd_get_next_chan(struct vpdma_dtd *dtd) +{ +	return (dtd->pkt_ctl >> DTD_NEXT_CHAN_SHFT) & DTD_NEXT_CHAN_MASK; +} + +static inline int dtd_get_frame_width(struct vpdma_dtd *dtd) +{ +	return dtd->frame_width_height >> DTD_FRAME_WIDTH_SHFT; +} + +static inline int dtd_get_frame_height(struct vpdma_dtd *dtd) +{ +	return dtd->frame_width_height & DTD_FRAME_HEIGHT_MASK; +} + +static inline int dtd_get_desc_write_addr(struct vpdma_dtd *dtd) +{ +	return dtd->desc_write_addr >> DTD_DESC_START_SHIFT; +} + +static inline bool dtd_get_write_desc(struct vpdma_dtd *dtd) +{ +	return (dtd->desc_write_addr >> DTD_WRITE_DESC_SHIFT) & +							DTD_WRITE_DESC_MASK; +} + +static inline bool dtd_get_drop_data(struct vpdma_dtd *dtd) +{ +	return (dtd->desc_write_addr >> DTD_DROP_DATA_SHIFT) & +							DTD_DROP_DATA_MASK; +} + +static inline bool dtd_get_use_desc(struct vpdma_dtd *dtd) +{ +	return dtd->desc_write_addr & DTD_USE_DESC_MASK; +} + +static inline int dtd_get_h_start(struct vpdma_dtd *dtd) +{ +	return dtd->start_h_v >> DTD_H_START_SHFT; +} + +static inline int dtd_get_v_start(struct vpdma_dtd *dtd) +{ +	return dtd->start_h_v & DTD_V_START_MASK; +} + +static inline int dtd_get_max_width(struct vpdma_dtd *dtd) +{ +	return (dtd->max_width_height >> DTD_MAX_WIDTH_SHFT) & +							DTD_MAX_WIDTH_MASK; +} + +static inline int dtd_get_max_height(struct vpdma_dtd *dtd) +{ +	return (dtd->max_width_height >> DTD_MAX_HEIGHT_SHFT) & +							DTD_MAX_HEIGHT_MASK; +} + +/* + * configuration descriptor + */ +struct vpdma_cfd { +	union { +		u32	dest_addr_offset; +		u32	w0; +	}; +	union { +		u32	block_len;		/* in words */ +		u32	w1; +	}; +	u32		payload_addr; +	u32		ctl_payload_len;	/* in words */ +}; + +/* Configuration descriptor specifics */ + +#define CFD_PKT_TYPE		0xb + +#define CFD_DIRECT		1 +#define CFD_INDIRECT		0 +#define CFD_CLS_ADB		0 +#define CFD_CLS_BLOCK		1 + +/* block_len */ +#define CFD__BLOCK_LEN_MASK	0xffff +#define CFD__BLOCK_LEN_SHFT	0 + +/* ctl_payload_len */ +#define CFD_PKT_TYPE_MASK	0x1f +#define CFD_PKT_TYPE_SHFT	27 +#define CFD_DIRECT_MASK		0x01 +#define CFD_DIRECT_SHFT		26 +#define CFD_CLASS_MASK		0x03 +#define CFD_CLASS_SHFT		24 +#define CFD_DEST_MASK		0xff +#define CFD_DEST_SHFT		16 +#define CFD_PAYLOAD_LEN_MASK	0xffff +#define CFD_PAYLOAD_LEN_SHFT	0 + +static inline u32 cfd_pkt_payload_len(bool direct, int cls, int dest, +		int payload_len) +{ +	return (CFD_PKT_TYPE << CFD_PKT_TYPE_SHFT) | +		(direct << CFD_DIRECT_SHFT) | +		(cls << CFD_CLASS_SHFT) | +		(dest << CFD_DEST_SHFT) | +		payload_len; +} + +static inline int cfd_get_pkt_type(struct vpdma_cfd *cfd) +{ +	return cfd->ctl_payload_len >> CFD_PKT_TYPE_SHFT; +} + +static inline bool cfd_get_direct(struct vpdma_cfd *cfd) +{ +	return (cfd->ctl_payload_len >> CFD_DIRECT_SHFT) & CFD_DIRECT_MASK; +} + +static inline bool cfd_get_class(struct vpdma_cfd *cfd) +{ +	return (cfd->ctl_payload_len >> CFD_CLASS_SHFT) & CFD_CLASS_MASK; +} + +static inline int cfd_get_dest(struct vpdma_cfd *cfd) +{ +	return (cfd->ctl_payload_len >> CFD_DEST_SHFT) & CFD_DEST_MASK; +} + +static inline int cfd_get_payload_len(struct vpdma_cfd *cfd) +{ +	return cfd->ctl_payload_len & CFD_PAYLOAD_LEN_MASK; +} + +/* + * control descriptor + */ +struct vpdma_ctd { +	union { +		u32	timer_value; +		u32	list_addr; +		u32	w0; +	}; +	union { +		u32	pixel_line_count; +		u32	list_size; +		u32	w1; +	}; +	union { +		u32	event; +		u32	fid_ctl; +		u32	w2; +	}; +	u32		type_source_ctl; +}; + +/* control descriptor types */ +#define CTD_TYPE_SYNC_ON_CLIENT		0 +#define CTD_TYPE_SYNC_ON_LIST		1 +#define CTD_TYPE_SYNC_ON_EXT		2 +#define CTD_TYPE_SYNC_ON_LM_TIMER	3 +#define CTD_TYPE_SYNC_ON_CHANNEL	4 +#define CTD_TYPE_CHNG_CLIENT_IRQ	5 +#define CTD_TYPE_SEND_IRQ		6 +#define CTD_TYPE_RELOAD_LIST		7 +#define CTD_TYPE_ABORT_CHANNEL		8 + +#define CTD_PKT_TYPE		0xc + +/* timer_value */ +#define CTD_TIMER_VALUE_MASK	0xffff +#define CTD_TIMER_VALUE_SHFT	0 + +/* pixel_line_count */ +#define CTD_PIXEL_COUNT_MASK	0xffff +#define CTD_PIXEL_COUNT_SHFT	16 +#define CTD_LINE_COUNT_MASK	0xffff +#define CTD_LINE_COUNT_SHFT	0 + +/* list_size */ +#define CTD_LIST_SIZE_MASK	0xffff +#define CTD_LIST_SIZE_SHFT	0 + +/* event */ +#define CTD_EVENT_MASK		0x0f +#define CTD_EVENT_SHFT		0 + +/* fid_ctl */ +#define CTD_FID2_MASK		0x03 +#define CTD_FID2_SHFT		4 +#define CTD_FID1_MASK		0x03 +#define CTD_FID1_SHFT		2 +#define CTD_FID0_MASK		0x03 +#define CTD_FID0_SHFT		0 + +/* type_source_ctl */ +#define CTD_PKT_TYPE_MASK	0x1f +#define CTD_PKT_TYPE_SHFT	27 +#define CTD_SOURCE_MASK		0xff +#define CTD_SOURCE_SHFT		16 +#define CTD_CONTROL_MASK	0x0f +#define CTD_CONTROL_SHFT	0 + +static inline u32 ctd_pixel_line_count(int pixel_count, int line_count) +{ +	return (pixel_count << CTD_PIXEL_COUNT_SHFT) | line_count; +} + +static inline u32 ctd_set_fid_ctl(int fid0, int fid1, int fid2) +{ +	return (fid2 << CTD_FID2_SHFT) | (fid1 << CTD_FID1_SHFT) | fid0; +} + +static inline u32 ctd_type_source_ctl(int source, int control) +{ +	return (CTD_PKT_TYPE << CTD_PKT_TYPE_SHFT) | +		(source << CTD_SOURCE_SHFT) | control; +} + +static inline u32 ctd_get_pixel_count(struct vpdma_ctd *ctd) +{ +	return ctd->pixel_line_count >> CTD_PIXEL_COUNT_SHFT; +} + +static inline int ctd_get_line_count(struct vpdma_ctd *ctd) +{ +	return ctd->pixel_line_count & CTD_LINE_COUNT_MASK; +} + +static inline int ctd_get_event(struct vpdma_ctd *ctd) +{ +	return ctd->event & CTD_EVENT_MASK; +} + +static inline int ctd_get_fid2_ctl(struct vpdma_ctd *ctd) +{ +	return (ctd->fid_ctl >> CTD_FID2_SHFT) & CTD_FID2_MASK; +} + +static inline int ctd_get_fid1_ctl(struct vpdma_ctd *ctd) +{ +	return (ctd->fid_ctl >> CTD_FID1_SHFT) & CTD_FID1_MASK; +} + +static inline int ctd_get_fid0_ctl(struct vpdma_ctd *ctd) +{ +	return ctd->fid_ctl & CTD_FID2_MASK; +} + +static inline int ctd_get_pkt_type(struct vpdma_ctd *ctd) +{ +	return ctd->type_source_ctl >> CTD_PKT_TYPE_SHFT; +} + +static inline int ctd_get_source(struct vpdma_ctd *ctd) +{ +	return (ctd->type_source_ctl >> CTD_SOURCE_SHFT) & CTD_SOURCE_MASK; +} + +static inline int ctd_get_ctl(struct vpdma_ctd *ctd) +{ +	return ctd->type_source_ctl & CTD_CONTROL_MASK; +} + +#endif diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c new file mode 100644 index 00000000000..972f43f6920 --- /dev/null +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -0,0 +1,2388 @@ +/* + * TI VPE mem2mem driver, based on the virtual v4l2-mem2mem example driver + * + * Copyright (c) 2013 Texas Instruments Inc. + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Pawel Osciak, <pawel@osciak.com> + * Marek Szyprowski, <m.szyprowski@samsung.com> + * + * Based on the virtual v4l2-mem2mem example device + * + * 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 + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioctl.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "vpdma.h" +#include "vpe_regs.h" +#include "sc.h" +#include "csc.h" + +#define VPE_MODULE_NAME "vpe" + +/* minimum and maximum frame sizes */ +#define MIN_W		32 +#define MIN_H		32 +#define MAX_W		1920 +#define MAX_H		1080 + +/* required alignments */ +#define S_ALIGN		0	/* multiple of 1 */ +#define H_ALIGN		1	/* multiple of 2 */ + +/* flags that indicate a format can be used for capture/output */ +#define VPE_FMT_TYPE_CAPTURE	(1 << 0) +#define VPE_FMT_TYPE_OUTPUT	(1 << 1) + +/* used as plane indices */ +#define VPE_MAX_PLANES	2 +#define VPE_LUMA	0 +#define VPE_CHROMA	1 + +/* per m2m context info */ +#define VPE_MAX_SRC_BUFS	3	/* need 3 src fields to de-interlace */ + +#define VPE_DEF_BUFS_PER_JOB	1	/* default one buffer per batch job */ + +/* + * each VPE context can need up to 3 config desciptors, 7 input descriptors, + * 3 output descriptors, and 10 control descriptors + */ +#define VPE_DESC_LIST_SIZE	(10 * VPDMA_DTD_DESC_SIZE +	\ +					13 * VPDMA_CFD_CTD_DESC_SIZE) + +#define vpe_dbg(vpedev, fmt, arg...)	\ +		dev_dbg((vpedev)->v4l2_dev.dev, fmt, ##arg) +#define vpe_err(vpedev, fmt, arg...)	\ +		dev_err((vpedev)->v4l2_dev.dev, fmt, ##arg) + +struct vpe_us_coeffs { +	unsigned short	anchor_fid0_c0; +	unsigned short	anchor_fid0_c1; +	unsigned short	anchor_fid0_c2; +	unsigned short	anchor_fid0_c3; +	unsigned short	interp_fid0_c0; +	unsigned short	interp_fid0_c1; +	unsigned short	interp_fid0_c2; +	unsigned short	interp_fid0_c3; +	unsigned short	anchor_fid1_c0; +	unsigned short	anchor_fid1_c1; +	unsigned short	anchor_fid1_c2; +	unsigned short	anchor_fid1_c3; +	unsigned short	interp_fid1_c0; +	unsigned short	interp_fid1_c1; +	unsigned short	interp_fid1_c2; +	unsigned short	interp_fid1_c3; +}; + +/* + * Default upsampler coefficients + */ +static const struct vpe_us_coeffs us_coeffs[] = { +	{ +		/* Coefficients for progressive input */ +		0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, +		0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8, +	}, +	{ +		/* Coefficients for Top Field Interlaced input */ +		0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3, +		/* Coefficients for Bottom Field Interlaced input */ +		0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9, +	}, +}; + +/* + * the following registers are for configuring some of the parameters of the + * motion and edge detection blocks inside DEI, these generally remain the same, + * these could be passed later via userspace if some one needs to tweak these. + */ +struct vpe_dei_regs { +	unsigned long mdt_spacial_freq_thr_reg;		/* VPE_DEI_REG2 */ +	unsigned long edi_config_reg;			/* VPE_DEI_REG3 */ +	unsigned long edi_lut_reg0;			/* VPE_DEI_REG4 */ +	unsigned long edi_lut_reg1;			/* VPE_DEI_REG5 */ +	unsigned long edi_lut_reg2;			/* VPE_DEI_REG6 */ +	unsigned long edi_lut_reg3;			/* VPE_DEI_REG7 */ +}; + +/* + * default expert DEI register values, unlikely to be modified. + */ +static const struct vpe_dei_regs dei_regs = { +	0x020C0804u, +	0x0118100Fu, +	0x08040200u, +	0x1010100Cu, +	0x10101010u, +	0x10101010u, +}; + +/* + * The port_data structure contains per-port data. + */ +struct vpe_port_data { +	enum vpdma_channel channel;	/* VPDMA channel */ +	u8	vb_index;		/* input frame f, f-1, f-2 index */ +	u8	vb_part;		/* plane index for co-panar formats */ +}; + +/* + * Define indices into the port_data tables + */ +#define VPE_PORT_LUMA1_IN	0 +#define VPE_PORT_CHROMA1_IN	1 +#define VPE_PORT_LUMA2_IN	2 +#define VPE_PORT_CHROMA2_IN	3 +#define VPE_PORT_LUMA3_IN	4 +#define VPE_PORT_CHROMA3_IN	5 +#define VPE_PORT_MV_IN		6 +#define VPE_PORT_MV_OUT		7 +#define VPE_PORT_LUMA_OUT	8 +#define VPE_PORT_CHROMA_OUT	9 +#define VPE_PORT_RGB_OUT	10 + +static const struct vpe_port_data port_data[11] = { +	[VPE_PORT_LUMA1_IN] = { +		.channel	= VPE_CHAN_LUMA1_IN, +		.vb_index	= 0, +		.vb_part	= VPE_LUMA, +	}, +	[VPE_PORT_CHROMA1_IN] = { +		.channel	= VPE_CHAN_CHROMA1_IN, +		.vb_index	= 0, +		.vb_part	= VPE_CHROMA, +	}, +	[VPE_PORT_LUMA2_IN] = { +		.channel	= VPE_CHAN_LUMA2_IN, +		.vb_index	= 1, +		.vb_part	= VPE_LUMA, +	}, +	[VPE_PORT_CHROMA2_IN] = { +		.channel	= VPE_CHAN_CHROMA2_IN, +		.vb_index	= 1, +		.vb_part	= VPE_CHROMA, +	}, +	[VPE_PORT_LUMA3_IN] = { +		.channel	= VPE_CHAN_LUMA3_IN, +		.vb_index	= 2, +		.vb_part	= VPE_LUMA, +	}, +	[VPE_PORT_CHROMA3_IN] = { +		.channel	= VPE_CHAN_CHROMA3_IN, +		.vb_index	= 2, +		.vb_part	= VPE_CHROMA, +	}, +	[VPE_PORT_MV_IN] = { +		.channel	= VPE_CHAN_MV_IN, +	}, +	[VPE_PORT_MV_OUT] = { +		.channel	= VPE_CHAN_MV_OUT, +	}, +	[VPE_PORT_LUMA_OUT] = { +		.channel	= VPE_CHAN_LUMA_OUT, +		.vb_part	= VPE_LUMA, +	}, +	[VPE_PORT_CHROMA_OUT] = { +		.channel	= VPE_CHAN_CHROMA_OUT, +		.vb_part	= VPE_CHROMA, +	}, +	[VPE_PORT_RGB_OUT] = { +		.channel	= VPE_CHAN_RGB_OUT, +		.vb_part	= VPE_LUMA, +	}, +}; + + +/* driver info for each of the supported video formats */ +struct vpe_fmt { +	char	*name;			/* human-readable name */ +	u32	fourcc;			/* standard format identifier */ +	u8	types;			/* CAPTURE and/or OUTPUT */ +	u8	coplanar;		/* set for unpacked Luma and Chroma */ +	/* vpdma format info for each plane */ +	struct vpdma_data_format const *vpdma_fmt[VPE_MAX_PLANES]; +}; + +static struct vpe_fmt vpe_formats[] = { +	{ +		.name		= "YUV 422 co-planar", +		.fourcc		= V4L2_PIX_FMT_NV16, +		.types		= VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, +		.coplanar	= 1, +		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444], +				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_C444], +				  }, +	}, +	{ +		.name		= "YUV 420 co-planar", +		.fourcc		= V4L2_PIX_FMT_NV12, +		.types		= VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, +		.coplanar	= 1, +		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420], +				    &vpdma_yuv_fmts[VPDMA_DATA_FMT_C420], +				  }, +	}, +	{ +		.name		= "YUYV 422 packed", +		.fourcc		= V4L2_PIX_FMT_YUYV, +		.types		= VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422], +				  }, +	}, +	{ +		.name		= "UYVY 422 packed", +		.fourcc		= V4L2_PIX_FMT_UYVY, +		.types		= VPE_FMT_TYPE_CAPTURE | VPE_FMT_TYPE_OUTPUT, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422], +				  }, +	}, +	{ +		.name		= "RGB888 packed", +		.fourcc		= V4L2_PIX_FMT_RGB24, +		.types		= VPE_FMT_TYPE_CAPTURE, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_RGB24], +				  }, +	}, +	{ +		.name		= "ARGB32", +		.fourcc		= V4L2_PIX_FMT_RGB32, +		.types		= VPE_FMT_TYPE_CAPTURE, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ARGB32], +				  }, +	}, +	{ +		.name		= "BGR888 packed", +		.fourcc		= V4L2_PIX_FMT_BGR24, +		.types		= VPE_FMT_TYPE_CAPTURE, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_BGR24], +				  }, +	}, +	{ +		.name		= "ABGR32", +		.fourcc		= V4L2_PIX_FMT_BGR32, +		.types		= VPE_FMT_TYPE_CAPTURE, +		.coplanar	= 0, +		.vpdma_fmt	= { &vpdma_rgb_fmts[VPDMA_DATA_FMT_ABGR32], +				  }, +	}, +}; + +/* + * per-queue, driver-specific private data. + * there is one source queue and one destination queue for each m2m context. + */ +struct vpe_q_data { +	unsigned int		width;				/* frame width */ +	unsigned int		height;				/* frame height */ +	unsigned int		bytesperline[VPE_MAX_PLANES];	/* bytes per line in memory */ +	enum v4l2_colorspace	colorspace; +	enum v4l2_field		field;				/* supported field value */ +	unsigned int		flags; +	unsigned int		sizeimage[VPE_MAX_PLANES];	/* image size in memory */ +	struct v4l2_rect	c_rect;				/* crop/compose rectangle */ +	struct vpe_fmt		*fmt;				/* format info */ +}; + +/* vpe_q_data flag bits */ +#define	Q_DATA_FRAME_1D		(1 << 0) +#define	Q_DATA_MODE_TILED	(1 << 1) +#define	Q_DATA_INTERLACED	(1 << 2) + +enum { +	Q_DATA_SRC = 0, +	Q_DATA_DST = 1, +}; + +/* find our format description corresponding to the passed v4l2_format */ +static struct vpe_fmt *find_format(struct v4l2_format *f) +{ +	struct vpe_fmt *fmt; +	unsigned int k; + +	for (k = 0; k < ARRAY_SIZE(vpe_formats); k++) { +		fmt = &vpe_formats[k]; +		if (fmt->fourcc == f->fmt.pix.pixelformat) +			return fmt; +	} + +	return NULL; +} + +/* + * there is one vpe_dev structure in the driver, it is shared by + * all instances. + */ +struct vpe_dev { +	struct v4l2_device	v4l2_dev; +	struct video_device	vfd; +	struct v4l2_m2m_dev	*m2m_dev; + +	atomic_t		num_instances;	/* count of driver instances */ +	dma_addr_t		loaded_mmrs;	/* shadow mmrs in device */ +	struct mutex		dev_mutex; +	spinlock_t		lock; + +	int			irq; +	void __iomem		*base; +	struct resource		*res; + +	struct vb2_alloc_ctx	*alloc_ctx; +	struct vpdma_data	*vpdma;		/* vpdma data handle */ +	struct sc_data		*sc;		/* scaler data handle */ +	struct csc_data		*csc;		/* csc data handle */ +}; + +/* + * There is one vpe_ctx structure for each m2m context. + */ +struct vpe_ctx { +	struct v4l2_fh		fh; +	struct vpe_dev		*dev; +	struct v4l2_m2m_ctx	*m2m_ctx; +	struct v4l2_ctrl_handler hdl; + +	unsigned int		field;			/* current field */ +	unsigned int		sequence;		/* current frame/field seq */ +	unsigned int		aborting;		/* abort after next irq */ + +	unsigned int		bufs_per_job;		/* input buffers per batch */ +	unsigned int		bufs_completed;		/* bufs done in this batch */ + +	struct vpe_q_data	q_data[2];		/* src & dst queue data */ +	struct vb2_buffer	*src_vbs[VPE_MAX_SRC_BUFS]; +	struct vb2_buffer	*dst_vb; + +	dma_addr_t		mv_buf_dma[2];		/* dma addrs of motion vector in/out bufs */ +	void			*mv_buf[2];		/* virtual addrs of motion vector bufs */ +	size_t			mv_buf_size;		/* current motion vector buffer size */ +	struct vpdma_buf	mmr_adb;		/* shadow reg addr/data block */ +	struct vpdma_buf	sc_coeff_h;		/* h coeff buffer */ +	struct vpdma_buf	sc_coeff_v;		/* v coeff buffer */ +	struct vpdma_desc_list	desc_list;		/* DMA descriptor list */ + +	bool			deinterlacing;		/* using de-interlacer */ +	bool			load_mmrs;		/* have new shadow reg values */ + +	unsigned int		src_mv_buf_selector; +}; + + +/* + * M2M devices get 2 queues. + * Return the queue given the type. + */ +static struct vpe_q_data *get_q_data(struct vpe_ctx *ctx, +				     enum v4l2_buf_type type) +{ +	switch (type) { +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: +	case V4L2_BUF_TYPE_VIDEO_OUTPUT: +		return &ctx->q_data[Q_DATA_SRC]; +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: +	case V4L2_BUF_TYPE_VIDEO_CAPTURE: +		return &ctx->q_data[Q_DATA_DST]; +	default: +		BUG(); +	} +	return NULL; +} + +static u32 read_reg(struct vpe_dev *dev, int offset) +{ +	return ioread32(dev->base + offset); +} + +static void write_reg(struct vpe_dev *dev, int offset, u32 value) +{ +	iowrite32(value, dev->base + offset); +} + +/* register field read/write helpers */ +static int get_field(u32 value, u32 mask, int shift) +{ +	return (value & (mask << shift)) >> shift; +} + +static int read_field_reg(struct vpe_dev *dev, int offset, u32 mask, int shift) +{ +	return get_field(read_reg(dev, offset), mask, shift); +} + +static void write_field(u32 *valp, u32 field, u32 mask, int shift) +{ +	u32 val = *valp; + +	val &= ~(mask << shift); +	val |= (field & mask) << shift; +	*valp = val; +} + +static void write_field_reg(struct vpe_dev *dev, int offset, u32 field, +		u32 mask, int shift) +{ +	u32 val = read_reg(dev, offset); + +	write_field(&val, field, mask, shift); + +	write_reg(dev, offset, val); +} + +/* + * DMA address/data block for the shadow registers + */ +struct vpe_mmr_adb { +	struct vpdma_adb_hdr	out_fmt_hdr; +	u32			out_fmt_reg[1]; +	u32			out_fmt_pad[3]; +	struct vpdma_adb_hdr	us1_hdr; +	u32			us1_regs[8]; +	struct vpdma_adb_hdr	us2_hdr; +	u32			us2_regs[8]; +	struct vpdma_adb_hdr	us3_hdr; +	u32			us3_regs[8]; +	struct vpdma_adb_hdr	dei_hdr; +	u32			dei_regs[8]; +	struct vpdma_adb_hdr	sc_hdr0; +	u32			sc_regs0[7]; +	u32			sc_pad0[1]; +	struct vpdma_adb_hdr	sc_hdr8; +	u32			sc_regs8[6]; +	u32			sc_pad8[2]; +	struct vpdma_adb_hdr	sc_hdr17; +	u32			sc_regs17[9]; +	u32			sc_pad17[3]; +	struct vpdma_adb_hdr	csc_hdr; +	u32			csc_regs[6]; +	u32			csc_pad[2]; +}; + +#define GET_OFFSET_TOP(ctx, obj, reg)	\ +	((obj)->res->start - ctx->dev->res->start + reg) + +#define VPE_SET_MMR_ADB_HDR(ctx, hdr, regs, offset_a)	\ +	VPDMA_SET_MMR_ADB_HDR(ctx->mmr_adb, vpe_mmr_adb, hdr, regs, offset_a) +/* + * Set the headers for all of the address/data block structures. + */ +static void init_adb_hdrs(struct vpe_ctx *ctx) +{ +	VPE_SET_MMR_ADB_HDR(ctx, out_fmt_hdr, out_fmt_reg, VPE_CLK_FORMAT_SELECT); +	VPE_SET_MMR_ADB_HDR(ctx, us1_hdr, us1_regs, VPE_US1_R0); +	VPE_SET_MMR_ADB_HDR(ctx, us2_hdr, us2_regs, VPE_US2_R0); +	VPE_SET_MMR_ADB_HDR(ctx, us3_hdr, us3_regs, VPE_US3_R0); +	VPE_SET_MMR_ADB_HDR(ctx, dei_hdr, dei_regs, VPE_DEI_FRAME_SIZE); +	VPE_SET_MMR_ADB_HDR(ctx, sc_hdr0, sc_regs0, +		GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC0)); +	VPE_SET_MMR_ADB_HDR(ctx, sc_hdr8, sc_regs8, +		GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC8)); +	VPE_SET_MMR_ADB_HDR(ctx, sc_hdr17, sc_regs17, +		GET_OFFSET_TOP(ctx, ctx->dev->sc, CFG_SC17)); +	VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, +		GET_OFFSET_TOP(ctx, ctx->dev->csc, CSC_CSC00)); +}; + +/* + * Allocate or re-allocate the motion vector DMA buffers + * There are two buffers, one for input and one for output. + * However, the roles are reversed after each field is processed. + * In other words, after each field is processed, the previous + * output (dst) MV buffer becomes the new input (src) MV buffer. + */ +static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size) +{ +	struct device *dev = ctx->dev->v4l2_dev.dev; + +	if (ctx->mv_buf_size == size) +		return 0; + +	if (ctx->mv_buf[0]) +		dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0], +			ctx->mv_buf_dma[0]); + +	if (ctx->mv_buf[1]) +		dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1], +			ctx->mv_buf_dma[1]); + +	if (size == 0) +		return 0; + +	ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0], +				GFP_KERNEL); +	if (!ctx->mv_buf[0]) { +		vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); +		return -ENOMEM; +	} + +	ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1], +				GFP_KERNEL); +	if (!ctx->mv_buf[1]) { +		vpe_err(ctx->dev, "failed to allocate motion vector buffer\n"); +		dma_free_coherent(dev, size, ctx->mv_buf[0], +			ctx->mv_buf_dma[0]); + +		return -ENOMEM; +	} + +	ctx->mv_buf_size = size; +	ctx->src_mv_buf_selector = 0; + +	return 0; +} + +static void free_mv_buffers(struct vpe_ctx *ctx) +{ +	realloc_mv_buffers(ctx, 0); +} + +/* + * While de-interlacing, we keep the two most recent input buffers + * around.  This function frees those two buffers when we have + * finished processing the current stream. + */ +static void free_vbs(struct vpe_ctx *ctx) +{ +	struct vpe_dev *dev = ctx->dev; +	unsigned long flags; + +	if (ctx->src_vbs[2] == NULL) +		return; + +	spin_lock_irqsave(&dev->lock, flags); +	if (ctx->src_vbs[2]) { +		v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE); +		v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE); +	} +	spin_unlock_irqrestore(&dev->lock, flags); +} + +/* + * Enable or disable the VPE clocks + */ +static void vpe_set_clock_enable(struct vpe_dev *dev, bool on) +{ +	u32 val = 0; + +	if (on) +		val = VPE_DATA_PATH_CLK_ENABLE | VPE_VPEDMA_CLK_ENABLE; +	write_reg(dev, VPE_CLK_ENABLE, val); +} + +static void vpe_top_reset(struct vpe_dev *dev) +{ + +	write_field_reg(dev, VPE_CLK_RESET, 1, VPE_DATA_PATH_CLK_RESET_MASK, +		VPE_DATA_PATH_CLK_RESET_SHIFT); + +	usleep_range(100, 150); + +	write_field_reg(dev, VPE_CLK_RESET, 0, VPE_DATA_PATH_CLK_RESET_MASK, +		VPE_DATA_PATH_CLK_RESET_SHIFT); +} + +static void vpe_top_vpdma_reset(struct vpe_dev *dev) +{ +	write_field_reg(dev, VPE_CLK_RESET, 1, VPE_VPDMA_CLK_RESET_MASK, +		VPE_VPDMA_CLK_RESET_SHIFT); + +	usleep_range(100, 150); + +	write_field_reg(dev, VPE_CLK_RESET, 0, VPE_VPDMA_CLK_RESET_MASK, +		VPE_VPDMA_CLK_RESET_SHIFT); +} + +/* + * Load the correct of upsampler coefficients into the shadow MMRs + */ +static void set_us_coefficients(struct vpe_ctx *ctx) +{ +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; +	u32 *us1_reg = &mmr_adb->us1_regs[0]; +	u32 *us2_reg = &mmr_adb->us2_regs[0]; +	u32 *us3_reg = &mmr_adb->us3_regs[0]; +	const unsigned short *cp, *end_cp; + +	cp = &us_coeffs[0].anchor_fid0_c0; + +	if (s_q_data->flags & Q_DATA_INTERLACED)	/* interlaced */ +		cp += sizeof(us_coeffs[0]) / sizeof(*cp); + +	end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp); + +	while (cp < end_cp) { +		write_field(us1_reg, *cp++, VPE_US_C0_MASK, VPE_US_C0_SHIFT); +		write_field(us1_reg, *cp++, VPE_US_C1_MASK, VPE_US_C1_SHIFT); +		*us2_reg++ = *us1_reg; +		*us3_reg++ = *us1_reg++; +	} +	ctx->load_mmrs = true; +} + +/* + * Set the upsampler config mode and the VPDMA line mode in the shadow MMRs. + */ +static void set_cfg_and_line_modes(struct vpe_ctx *ctx) +{ +	struct vpe_fmt *fmt = ctx->q_data[Q_DATA_SRC].fmt; +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	u32 *us1_reg0 = &mmr_adb->us1_regs[0]; +	u32 *us2_reg0 = &mmr_adb->us2_regs[0]; +	u32 *us3_reg0 = &mmr_adb->us3_regs[0]; +	int line_mode = 1; +	int cfg_mode = 1; + +	/* +	 * Cfg Mode 0: YUV420 source, enable upsampler, DEI is de-interlacing. +	 * Cfg Mode 1: YUV422 source, disable upsampler, DEI is de-interlacing. +	 */ + +	if (fmt->fourcc == V4L2_PIX_FMT_NV12) { +		cfg_mode = 0; +		line_mode = 0;		/* double lines to line buffer */ +	} + +	write_field(us1_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); +	write_field(us2_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); +	write_field(us3_reg0, cfg_mode, VPE_US_MODE_MASK, VPE_US_MODE_SHIFT); + +	/* regs for now */ +	vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN); +	vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN); +	vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN); + +	/* frame start for input luma */ +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_LUMA1_IN); +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_LUMA2_IN); +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_LUMA3_IN); + +	/* frame start for input chroma */ +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_CHROMA1_IN); +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_CHROMA2_IN); +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_CHROMA3_IN); + +	/* frame start for MV in client */ +	vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE, +		VPE_CHAN_MV_IN); + +	ctx->load_mmrs = true; +} + +/* + * Set the shadow registers that are modified when the source + * format changes. + */ +static void set_src_registers(struct vpe_ctx *ctx) +{ +	set_us_coefficients(ctx); +} + +/* + * Set the shadow registers that are modified when the destination + * format changes. + */ +static void set_dst_registers(struct vpe_ctx *ctx) +{ +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	enum v4l2_colorspace clrspc = ctx->q_data[Q_DATA_DST].colorspace; +	struct vpe_fmt *fmt = ctx->q_data[Q_DATA_DST].fmt; +	u32 val = 0; + +	if (clrspc == V4L2_COLORSPACE_SRGB) +		val |= VPE_RGB_OUT_SELECT; +	else if (fmt->fourcc == V4L2_PIX_FMT_NV16) +		val |= VPE_COLOR_SEPARATE_422; + +	/* +	 * the source of CHR_DS and CSC is always the scaler, irrespective of +	 * whether it's used or not +	 */ +	val |= VPE_DS_SRC_DEI_SCALER | VPE_CSC_SRC_DEI_SCALER; + +	if (fmt->fourcc != V4L2_PIX_FMT_NV12) +		val |= VPE_DS_BYPASS; + +	mmr_adb->out_fmt_reg[0] = val; + +	ctx->load_mmrs = true; +} + +/* + * Set the de-interlacer shadow register values + */ +static void set_dei_regs(struct vpe_ctx *ctx) +{ +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC]; +	unsigned int src_h = s_q_data->c_rect.height; +	unsigned int src_w = s_q_data->c_rect.width; +	u32 *dei_mmr0 = &mmr_adb->dei_regs[0]; +	bool deinterlace = true; +	u32 val = 0; + +	/* +	 * according to TRM, we should set DEI in progressive bypass mode when +	 * the input content is progressive, however, DEI is bypassed correctly +	 * for both progressive and interlace content in interlace bypass mode. +	 * It has been recommended not to use progressive bypass mode. +	 */ +	if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) || +			!(s_q_data->flags & Q_DATA_INTERLACED)) { +		deinterlace = false; +		val = VPE_DEI_INTERLACE_BYPASS; +	} + +	src_h = deinterlace ? src_h * 2 : src_h; + +	val |= (src_h << VPE_DEI_HEIGHT_SHIFT) | +		(src_w << VPE_DEI_WIDTH_SHIFT) | +		VPE_DEI_FIELD_FLUSH; + +	*dei_mmr0 = val; + +	ctx->load_mmrs = true; +} + +static void set_dei_shadow_registers(struct vpe_ctx *ctx) +{ +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	u32 *dei_mmr = &mmr_adb->dei_regs[0]; +	const struct vpe_dei_regs *cur = &dei_regs; + +	dei_mmr[2]  = cur->mdt_spacial_freq_thr_reg; +	dei_mmr[3]  = cur->edi_config_reg; +	dei_mmr[4]  = cur->edi_lut_reg0; +	dei_mmr[5]  = cur->edi_lut_reg1; +	dei_mmr[6]  = cur->edi_lut_reg2; +	dei_mmr[7]  = cur->edi_lut_reg3; + +	ctx->load_mmrs = true; +} + +/* + * Set the shadow registers whose values are modified when either the + * source or destination format is changed. + */ +static int set_srcdst_params(struct vpe_ctx *ctx) +{ +	struct vpe_q_data *s_q_data =  &ctx->q_data[Q_DATA_SRC]; +	struct vpe_q_data *d_q_data =  &ctx->q_data[Q_DATA_DST]; +	struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr; +	unsigned int src_w = s_q_data->c_rect.width; +	unsigned int src_h = s_q_data->c_rect.height; +	unsigned int dst_w = d_q_data->c_rect.width; +	unsigned int dst_h = d_q_data->c_rect.height; +	size_t mv_buf_size; +	int ret; + +	ctx->sequence = 0; +	ctx->field = V4L2_FIELD_TOP; + +	if ((s_q_data->flags & Q_DATA_INTERLACED) && +			!(d_q_data->flags & Q_DATA_INTERLACED)) { +		int bytes_per_line; +		const struct vpdma_data_format *mv = +			&vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; + +		/* +		 * we make sure that the source image has a 16 byte aligned +		 * stride, we need to do the same for the motion vector buffer +		 * by aligning it's stride to the next 16 byte boundry. this +		 * extra space will not be used by the de-interlacer, but will +		 * ensure that vpdma operates correctly +		 */ +		bytes_per_line = ALIGN((s_q_data->width * mv->depth) >> 3, +					VPDMA_STRIDE_ALIGN); +		mv_buf_size = bytes_per_line * s_q_data->height; + +		ctx->deinterlacing = 1; +		src_h <<= 1; +	} else { +		ctx->deinterlacing = 0; +		mv_buf_size = 0; +	} + +	free_vbs(ctx); + +	ret = realloc_mv_buffers(ctx, mv_buf_size); +	if (ret) +		return ret; + +	set_cfg_and_line_modes(ctx); +	set_dei_regs(ctx); + +	csc_set_coeff(ctx->dev->csc, &mmr_adb->csc_regs[0], +		s_q_data->colorspace, d_q_data->colorspace); + +	sc_set_hs_coeffs(ctx->dev->sc, ctx->sc_coeff_h.addr, src_w, dst_w); +	sc_set_vs_coeffs(ctx->dev->sc, ctx->sc_coeff_v.addr, src_h, dst_h); + +	sc_config_scaler(ctx->dev->sc, &mmr_adb->sc_regs0[0], +		&mmr_adb->sc_regs8[0], &mmr_adb->sc_regs17[0], +		src_w, src_h, dst_w, dst_h); + +	return 0; +} + +/* + * Return the vpe_ctx structure for a given struct file + */ +static struct vpe_ctx *file2ctx(struct file *file) +{ +	return container_of(file->private_data, struct vpe_ctx, fh); +} + +/* + * mem2mem callbacks + */ + +/** + * job_ready() - check whether an instance is ready to be scheduled to run + */ +static int job_ready(void *priv) +{ +	struct vpe_ctx *ctx = priv; +	int needed = ctx->bufs_per_job; + +	if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) +		needed += 2;	/* need additional two most recent fields */ + +	if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed) +		return 0; + +	if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed) +		return 0; + +	return 1; +} + +static void job_abort(void *priv) +{ +	struct vpe_ctx *ctx = priv; + +	/* Will cancel the transaction in the next interrupt handler */ +	ctx->aborting = 1; +} + +/* + * Lock access to the device + */ +static void vpe_lock(void *priv) +{ +	struct vpe_ctx *ctx = priv; +	struct vpe_dev *dev = ctx->dev; +	mutex_lock(&dev->dev_mutex); +} + +static void vpe_unlock(void *priv) +{ +	struct vpe_ctx *ctx = priv; +	struct vpe_dev *dev = ctx->dev; +	mutex_unlock(&dev->dev_mutex); +} + +static void vpe_dump_regs(struct vpe_dev *dev) +{ +#define DUMPREG(r) vpe_dbg(dev, "%-35s %08x\n", #r, read_reg(dev, VPE_##r)) + +	vpe_dbg(dev, "VPE Registers:\n"); + +	DUMPREG(PID); +	DUMPREG(SYSCONFIG); +	DUMPREG(INT0_STATUS0_RAW); +	DUMPREG(INT0_STATUS0); +	DUMPREG(INT0_ENABLE0); +	DUMPREG(INT0_STATUS1_RAW); +	DUMPREG(INT0_STATUS1); +	DUMPREG(INT0_ENABLE1); +	DUMPREG(CLK_ENABLE); +	DUMPREG(CLK_RESET); +	DUMPREG(CLK_FORMAT_SELECT); +	DUMPREG(CLK_RANGE_MAP); +	DUMPREG(US1_R0); +	DUMPREG(US1_R1); +	DUMPREG(US1_R2); +	DUMPREG(US1_R3); +	DUMPREG(US1_R4); +	DUMPREG(US1_R5); +	DUMPREG(US1_R6); +	DUMPREG(US1_R7); +	DUMPREG(US2_R0); +	DUMPREG(US2_R1); +	DUMPREG(US2_R2); +	DUMPREG(US2_R3); +	DUMPREG(US2_R4); +	DUMPREG(US2_R5); +	DUMPREG(US2_R6); +	DUMPREG(US2_R7); +	DUMPREG(US3_R0); +	DUMPREG(US3_R1); +	DUMPREG(US3_R2); +	DUMPREG(US3_R3); +	DUMPREG(US3_R4); +	DUMPREG(US3_R5); +	DUMPREG(US3_R6); +	DUMPREG(US3_R7); +	DUMPREG(DEI_FRAME_SIZE); +	DUMPREG(MDT_BYPASS); +	DUMPREG(MDT_SF_THRESHOLD); +	DUMPREG(EDI_CONFIG); +	DUMPREG(DEI_EDI_LUT_R0); +	DUMPREG(DEI_EDI_LUT_R1); +	DUMPREG(DEI_EDI_LUT_R2); +	DUMPREG(DEI_EDI_LUT_R3); +	DUMPREG(DEI_FMD_WINDOW_R0); +	DUMPREG(DEI_FMD_WINDOW_R1); +	DUMPREG(DEI_FMD_CONTROL_R0); +	DUMPREG(DEI_FMD_CONTROL_R1); +	DUMPREG(DEI_FMD_STATUS_R0); +	DUMPREG(DEI_FMD_STATUS_R1); +	DUMPREG(DEI_FMD_STATUS_R2); +#undef DUMPREG + +	sc_dump_regs(dev->sc); +	csc_dump_regs(dev->csc); +} + +static void add_out_dtd(struct vpe_ctx *ctx, int port) +{ +	struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_DST]; +	const struct vpe_port_data *p_data = &port_data[port]; +	struct vb2_buffer *vb = ctx->dst_vb; +	struct vpe_fmt *fmt = q_data->fmt; +	const struct vpdma_data_format *vpdma_fmt; +	int mv_buf_selector = !ctx->src_mv_buf_selector; +	dma_addr_t dma_addr; +	u32 flags = 0; + +	if (port == VPE_PORT_MV_OUT) { +		vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; +		dma_addr = ctx->mv_buf_dma[mv_buf_selector]; +	} else { +		/* to incorporate interleaved formats */ +		int plane = fmt->coplanar ? p_data->vb_part : 0; + +		vpdma_fmt = fmt->vpdma_fmt[plane]; +		dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); +		if (!dma_addr) { +			vpe_err(ctx->dev, +				"acquiring output buffer(%d) dma_addr failed\n", +				port); +			return; +		} +	} + +	if (q_data->flags & Q_DATA_FRAME_1D) +		flags |= VPDMA_DATA_FRAME_1D; +	if (q_data->flags & Q_DATA_MODE_TILED) +		flags |= VPDMA_DATA_MODE_TILED; + +	vpdma_add_out_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, +		vpdma_fmt, dma_addr, p_data->channel, flags); +} + +static void add_in_dtd(struct vpe_ctx *ctx, int port) +{ +	struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC]; +	const struct vpe_port_data *p_data = &port_data[port]; +	struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index]; +	struct vpe_fmt *fmt = q_data->fmt; +	const struct vpdma_data_format *vpdma_fmt; +	int mv_buf_selector = ctx->src_mv_buf_selector; +	int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM; +	int frame_width, frame_height; +	dma_addr_t dma_addr; +	u32 flags = 0; + +	if (port == VPE_PORT_MV_IN) { +		vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV]; +		dma_addr = ctx->mv_buf_dma[mv_buf_selector]; +	} else { +		/* to incorporate interleaved formats */ +		int plane = fmt->coplanar ? p_data->vb_part : 0; + +		vpdma_fmt = fmt->vpdma_fmt[plane]; + +		dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane); +		if (!dma_addr) { +			vpe_err(ctx->dev, +				"acquiring input buffer(%d) dma_addr failed\n", +				port); +			return; +		} +	} + +	if (q_data->flags & Q_DATA_FRAME_1D) +		flags |= VPDMA_DATA_FRAME_1D; +	if (q_data->flags & Q_DATA_MODE_TILED) +		flags |= VPDMA_DATA_MODE_TILED; + +	frame_width = q_data->c_rect.width; +	frame_height = q_data->c_rect.height; + +	if (p_data->vb_part && fmt->fourcc == V4L2_PIX_FMT_NV12) +		frame_height /= 2; + +	vpdma_add_in_dtd(&ctx->desc_list, q_data->width, &q_data->c_rect, +		vpdma_fmt, dma_addr, p_data->channel, field, flags, frame_width, +		frame_height, 0, 0); +} + +/* + * Enable the expected IRQ sources + */ +static void enable_irqs(struct vpe_ctx *ctx) +{ +	write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE); +	write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT | +				VPE_DS1_UV_ERROR_INT); + +	vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, true); +} + +static void disable_irqs(struct vpe_ctx *ctx) +{ +	write_reg(ctx->dev, VPE_INT0_ENABLE0_CLR, 0xffffffff); +	write_reg(ctx->dev, VPE_INT0_ENABLE1_CLR, 0xffffffff); + +	vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, false); +} + +/* device_run() - prepares and starts the device + * + * This function is only called when both the source and destination + * buffers are in place. + */ +static void device_run(void *priv) +{ +	struct vpe_ctx *ctx = priv; +	struct sc_data *sc = ctx->dev->sc; +	struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; + +	if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { +		ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); +		WARN_ON(ctx->src_vbs[2] == NULL); +		ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); +		WARN_ON(ctx->src_vbs[1] == NULL); +	} + +	ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); +	WARN_ON(ctx->src_vbs[0] == NULL); +	ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); +	WARN_ON(ctx->dst_vb == NULL); + +	/* config descriptors */ +	if (ctx->dev->loaded_mmrs != ctx->mmr_adb.dma_addr || ctx->load_mmrs) { +		vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->mmr_adb); +		vpdma_add_cfd_adb(&ctx->desc_list, CFD_MMR_CLIENT, &ctx->mmr_adb); +		ctx->dev->loaded_mmrs = ctx->mmr_adb.dma_addr; +		ctx->load_mmrs = false; +	} + +	if (sc->loaded_coeff_h != ctx->sc_coeff_h.dma_addr || +			sc->load_coeff_h) { +		vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_h); +		vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, +			&ctx->sc_coeff_h, 0); + +		sc->loaded_coeff_h = ctx->sc_coeff_h.dma_addr; +		sc->load_coeff_h = false; +	} + +	if (sc->loaded_coeff_v != ctx->sc_coeff_v.dma_addr || +			sc->load_coeff_v) { +		vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->sc_coeff_v); +		vpdma_add_cfd_block(&ctx->desc_list, CFD_SC_CLIENT, +			&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE >> 4); + +		sc->loaded_coeff_v = ctx->sc_coeff_v.dma_addr; +		sc->load_coeff_v = false; +	} + +	/* output data descriptors */ +	if (ctx->deinterlacing) +		add_out_dtd(ctx, VPE_PORT_MV_OUT); + +	if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { +		add_out_dtd(ctx, VPE_PORT_RGB_OUT); +	} else { +		add_out_dtd(ctx, VPE_PORT_LUMA_OUT); +		if (d_q_data->fmt->coplanar) +			add_out_dtd(ctx, VPE_PORT_CHROMA_OUT); +	} + +	/* input data descriptors */ +	if (ctx->deinterlacing) { +		add_in_dtd(ctx, VPE_PORT_LUMA3_IN); +		add_in_dtd(ctx, VPE_PORT_CHROMA3_IN); + +		add_in_dtd(ctx, VPE_PORT_LUMA2_IN); +		add_in_dtd(ctx, VPE_PORT_CHROMA2_IN); +	} + +	add_in_dtd(ctx, VPE_PORT_LUMA1_IN); +	add_in_dtd(ctx, VPE_PORT_CHROMA1_IN); + +	if (ctx->deinterlacing) +		add_in_dtd(ctx, VPE_PORT_MV_IN); + +	/* sync on channel control descriptors for input ports */ +	vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN); +	vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN); + +	if (ctx->deinterlacing) { +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_LUMA2_IN); +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_CHROMA2_IN); + +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_LUMA3_IN); +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_CHROMA3_IN); + +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN); +	} + +	/* sync on channel control descriptors for output ports */ +	if (d_q_data->colorspace == V4L2_COLORSPACE_SRGB) { +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_RGB_OUT); +	} else { +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +			VPE_CHAN_LUMA_OUT); +		if (d_q_data->fmt->coplanar) +			vpdma_add_sync_on_channel_ctd(&ctx->desc_list, +				VPE_CHAN_CHROMA_OUT); +	} + +	if (ctx->deinterlacing) +		vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT); + +	enable_irqs(ctx); + +	vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf); +	vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list); +} + +static void dei_error(struct vpe_ctx *ctx) +{ +	dev_warn(ctx->dev->v4l2_dev.dev, +		"received DEI error interrupt\n"); +} + +static void ds1_uv_error(struct vpe_ctx *ctx) +{ +	dev_warn(ctx->dev->v4l2_dev.dev, +		"received downsampler error interrupt\n"); +} + +static irqreturn_t vpe_irq(int irq_vpe, void *data) +{ +	struct vpe_dev *dev = (struct vpe_dev *)data; +	struct vpe_ctx *ctx; +	struct vpe_q_data *d_q_data; +	struct vb2_buffer *s_vb, *d_vb; +	struct v4l2_buffer *s_buf, *d_buf; +	unsigned long flags; +	u32 irqst0, irqst1; + +	irqst0 = read_reg(dev, VPE_INT0_STATUS0); +	if (irqst0) { +		write_reg(dev, VPE_INT0_STATUS0_CLR, irqst0); +		vpe_dbg(dev, "INT0_STATUS0 = 0x%08x\n", irqst0); +	} + +	irqst1 = read_reg(dev, VPE_INT0_STATUS1); +	if (irqst1) { +		write_reg(dev, VPE_INT0_STATUS1_CLR, irqst1); +		vpe_dbg(dev, "INT0_STATUS1 = 0x%08x\n", irqst1); +	} + +	ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); +	if (!ctx) { +		vpe_err(dev, "instance released before end of transaction\n"); +		goto handled; +	} + +	if (irqst1) { +		if (irqst1 & VPE_DEI_ERROR_INT) { +			irqst1 &= ~VPE_DEI_ERROR_INT; +			dei_error(ctx); +		} +		if (irqst1 & VPE_DS1_UV_ERROR_INT) { +			irqst1 &= ~VPE_DS1_UV_ERROR_INT; +			ds1_uv_error(ctx); +		} +	} + +	if (irqst0) { +		if (irqst0 & VPE_INT0_LIST0_COMPLETE) +			vpdma_clear_list_stat(ctx->dev->vpdma); + +		irqst0 &= ~(VPE_INT0_LIST0_COMPLETE); +	} + +	if (irqst0 | irqst1) { +		dev_warn(dev->v4l2_dev.dev, "Unexpected interrupt: " +			"INT0_STATUS0 = 0x%08x, INT0_STATUS1 = 0x%08x\n", +			irqst0, irqst1); +	} + +	disable_irqs(ctx); + +	vpdma_unmap_desc_buf(dev->vpdma, &ctx->desc_list.buf); +	vpdma_unmap_desc_buf(dev->vpdma, &ctx->mmr_adb); +	vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_h); +	vpdma_unmap_desc_buf(dev->vpdma, &ctx->sc_coeff_v); + +	vpdma_reset_desc_list(&ctx->desc_list); + +	 /* the previous dst mv buffer becomes the next src mv buffer */ +	ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector; + +	if (ctx->aborting) +		goto finished; + +	s_vb = ctx->src_vbs[0]; +	d_vb = ctx->dst_vb; +	s_buf = &s_vb->v4l2_buf; +	d_buf = &d_vb->v4l2_buf; + +	d_buf->flags = s_buf->flags; + +	d_buf->timestamp = s_buf->timestamp; +	if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) +		d_buf->timecode = s_buf->timecode; + +	d_buf->sequence = ctx->sequence; + +	d_q_data = &ctx->q_data[Q_DATA_DST]; +	if (d_q_data->flags & Q_DATA_INTERLACED) { +		d_buf->field = ctx->field; +		if (ctx->field == V4L2_FIELD_BOTTOM) { +			ctx->sequence++; +			ctx->field = V4L2_FIELD_TOP; +		} else { +			WARN_ON(ctx->field != V4L2_FIELD_TOP); +			ctx->field = V4L2_FIELD_BOTTOM; +		} +	} else { +		d_buf->field = V4L2_FIELD_NONE; +		ctx->sequence++; +	} + +	if (ctx->deinterlacing) +		s_vb = ctx->src_vbs[2]; + +	spin_lock_irqsave(&dev->lock, flags); +	v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE); +	v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE); +	spin_unlock_irqrestore(&dev->lock, flags); + +	if (ctx->deinterlacing) { +		ctx->src_vbs[2] = ctx->src_vbs[1]; +		ctx->src_vbs[1] = ctx->src_vbs[0]; +	} + +	ctx->bufs_completed++; +	if (ctx->bufs_completed < ctx->bufs_per_job) { +		device_run(ctx); +		goto handled; +	} + +finished: +	vpe_dbg(ctx->dev, "finishing transaction\n"); +	ctx->bufs_completed = 0; +	v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); +handled: +	return IRQ_HANDLED; +} + +/* + * video ioctls + */ +static int vpe_querycap(struct file *file, void *priv, +			struct v4l2_capability *cap) +{ +	strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1); +	strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1); +	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", +		VPE_MODULE_NAME); +	cap->device_caps  = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; +	return 0; +} + +static int __enum_fmt(struct v4l2_fmtdesc *f, u32 type) +{ +	int i, index; +	struct vpe_fmt *fmt = NULL; + +	index = 0; +	for (i = 0; i < ARRAY_SIZE(vpe_formats); ++i) { +		if (vpe_formats[i].types & type) { +			if (index == f->index) { +				fmt = &vpe_formats[i]; +				break; +			} +			index++; +		} +	} + +	if (!fmt) +		return -EINVAL; + +	strncpy(f->description, fmt->name, sizeof(f->description) - 1); +	f->pixelformat = fmt->fourcc; +	return 0; +} + +static int vpe_enum_fmt(struct file *file, void *priv, +				struct v4l2_fmtdesc *f) +{ +	if (V4L2_TYPE_IS_OUTPUT(f->type)) +		return __enum_fmt(f, VPE_FMT_TYPE_OUTPUT); + +	return __enum_fmt(f, VPE_FMT_TYPE_CAPTURE); +} + +static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ +	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; +	struct vpe_ctx *ctx = file2ctx(file); +	struct vb2_queue *vq; +	struct vpe_q_data *q_data; +	int i; + +	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	if (!vq) +		return -EINVAL; + +	q_data = get_q_data(ctx, f->type); + +	pix->width = q_data->width; +	pix->height = q_data->height; +	pix->pixelformat = q_data->fmt->fourcc; +	pix->field = q_data->field; + +	if (V4L2_TYPE_IS_OUTPUT(f->type)) { +		pix->colorspace = q_data->colorspace; +	} else { +		struct vpe_q_data *s_q_data; + +		/* get colorspace from the source queue */ +		s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + +		pix->colorspace = s_q_data->colorspace; +	} + +	pix->num_planes = q_data->fmt->coplanar ? 2 : 1; + +	for (i = 0; i < pix->num_planes; i++) { +		pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; +		pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; +	} + +	return 0; +} + +static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, +		       struct vpe_fmt *fmt, int type) +{ +	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; +	struct v4l2_plane_pix_format *plane_fmt; +	unsigned int w_align; +	int i, depth, depth_bytes; + +	if (!fmt || !(fmt->types & type)) { +		vpe_err(ctx->dev, "Fourcc format (0x%08x) invalid.\n", +			pix->pixelformat); +		return -EINVAL; +	} + +	if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE) +		pix->field = V4L2_FIELD_NONE; + +	depth = fmt->vpdma_fmt[VPE_LUMA]->depth; + +	/* +	 * the line stride should 16 byte aligned for VPDMA to work, based on +	 * the bytes per pixel, figure out how much the width should be aligned +	 * to make sure line stride is 16 byte aligned +	 */ +	depth_bytes = depth >> 3; + +	if (depth_bytes == 3) +		/* +		 * if bpp is 3(as in some RGB formats), the pixel width doesn't +		 * really help in ensuring line stride is 16 byte aligned +		 */ +		w_align = 4; +	else +		/* +		 * for the remainder bpp(4, 2 and 1), the pixel width alignment +		 * can ensure a line stride alignment of 16 bytes. For example, +		 * if bpp is 2, then the line stride can be 16 byte aligned if +		 * the width is 8 byte aligned +		 */ +		w_align = order_base_2(VPDMA_DESC_ALIGN / depth_bytes); + +	v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align, +			      &pix->height, MIN_H, MAX_H, H_ALIGN, +			      S_ALIGN); + +	pix->num_planes = fmt->coplanar ? 2 : 1; +	pix->pixelformat = fmt->fourcc; + +	if (!pix->colorspace) { +		if (fmt->fourcc == V4L2_PIX_FMT_RGB24 || +				fmt->fourcc == V4L2_PIX_FMT_BGR24 || +				fmt->fourcc == V4L2_PIX_FMT_RGB32 || +				fmt->fourcc == V4L2_PIX_FMT_BGR32) { +			pix->colorspace = V4L2_COLORSPACE_SRGB; +		} else { +			if (pix->height > 1280)	/* HD */ +				pix->colorspace = V4L2_COLORSPACE_REC709; +			else			/* SD */ +				pix->colorspace = V4L2_COLORSPACE_SMPTE170M; +		} +	} + +	memset(pix->reserved, 0, sizeof(pix->reserved)); +	for (i = 0; i < pix->num_planes; i++) { +		plane_fmt = &pix->plane_fmt[i]; +		depth = fmt->vpdma_fmt[i]->depth; + +		if (i == VPE_LUMA) +			plane_fmt->bytesperline = (pix->width * depth) >> 3; +		else +			plane_fmt->bytesperline = pix->width; + +		plane_fmt->sizeimage = +				(pix->height * pix->width * depth) >> 3; + +		memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); +	} + +	return 0; +} + +static int vpe_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ +	struct vpe_ctx *ctx = file2ctx(file); +	struct vpe_fmt *fmt = find_format(f); + +	if (V4L2_TYPE_IS_OUTPUT(f->type)) +		return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_OUTPUT); +	else +		return __vpe_try_fmt(ctx, f, fmt, VPE_FMT_TYPE_CAPTURE); +} + +static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) +{ +	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; +	struct v4l2_plane_pix_format *plane_fmt; +	struct vpe_q_data *q_data; +	struct vb2_queue *vq; +	int i; + +	vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); +	if (!vq) +		return -EINVAL; + +	if (vb2_is_busy(vq)) { +		vpe_err(ctx->dev, "queue busy\n"); +		return -EBUSY; +	} + +	q_data = get_q_data(ctx, f->type); +	if (!q_data) +		return -EINVAL; + +	q_data->fmt		= find_format(f); +	q_data->width		= pix->width; +	q_data->height		= pix->height; +	q_data->colorspace	= pix->colorspace; +	q_data->field		= pix->field; + +	for (i = 0; i < pix->num_planes; i++) { +		plane_fmt = &pix->plane_fmt[i]; + +		q_data->bytesperline[i]	= plane_fmt->bytesperline; +		q_data->sizeimage[i]	= plane_fmt->sizeimage; +	} + +	q_data->c_rect.left	= 0; +	q_data->c_rect.top	= 0; +	q_data->c_rect.width	= q_data->width; +	q_data->c_rect.height	= q_data->height; + +	if (q_data->field == V4L2_FIELD_ALTERNATE) +		q_data->flags |= Q_DATA_INTERLACED; +	else +		q_data->flags &= ~Q_DATA_INTERLACED; + +	vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d", +		f->type, q_data->width, q_data->height, q_data->fmt->fourcc, +		q_data->bytesperline[VPE_LUMA]); +	if (q_data->fmt->coplanar) +		vpe_dbg(ctx->dev, " bpl_uv %d\n", +			q_data->bytesperline[VPE_CHROMA]); + +	return 0; +} + +static int vpe_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ +	int ret; +	struct vpe_ctx *ctx = file2ctx(file); + +	ret = vpe_try_fmt(file, priv, f); +	if (ret) +		return ret; + +	ret = __vpe_s_fmt(ctx, f); +	if (ret) +		return ret; + +	if (V4L2_TYPE_IS_OUTPUT(f->type)) +		set_src_registers(ctx); +	else +		set_dst_registers(ctx); + +	return set_srcdst_params(ctx); +} + +static int __vpe_try_selection(struct vpe_ctx *ctx, struct v4l2_selection *s) +{ +	struct vpe_q_data *q_data; + +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) +		return -EINVAL; + +	q_data = get_q_data(ctx, s->type); +	if (!q_data) +		return -EINVAL; + +	switch (s->target) { +	case V4L2_SEL_TGT_COMPOSE: +		/* +		 * COMPOSE target is only valid for capture buffer type, return +		 * error for output buffer type +		 */ +		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +			return -EINVAL; +		break; +	case V4L2_SEL_TGT_CROP: +		/* +		 * CROP target is only valid for output buffer type, return +		 * error for capture buffer type +		 */ +		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +			return -EINVAL; +		break; +	/* +	 * bound and default crop/compose targets are invalid targets to +	 * try/set +	 */ +	default: +		return -EINVAL; +	} + +	if (s->r.top < 0 || s->r.left < 0) { +		vpe_err(ctx->dev, "negative values for top and left\n"); +		s->r.top = s->r.left = 0; +	} + +	v4l_bound_align_image(&s->r.width, MIN_W, q_data->width, 1, +		&s->r.height, MIN_H, q_data->height, H_ALIGN, S_ALIGN); + +	/* adjust left/top if cropping rectangle is out of bounds */ +	if (s->r.left + s->r.width > q_data->width) +		s->r.left = q_data->width - s->r.width; +	if (s->r.top + s->r.height > q_data->height) +		s->r.top = q_data->height - s->r.height; + +	return 0; +} + +static int vpe_g_selection(struct file *file, void *fh, +		struct v4l2_selection *s) +{ +	struct vpe_ctx *ctx = file2ctx(file); +	struct vpe_q_data *q_data; +	bool use_c_rect = false; + +	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && +	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)) +		return -EINVAL; + +	q_data = get_q_data(ctx, s->type); +	if (!q_data) +		return -EINVAL; + +	switch (s->target) { +	case V4L2_SEL_TGT_COMPOSE_DEFAULT: +	case V4L2_SEL_TGT_COMPOSE_BOUNDS: +		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +			return -EINVAL; +		break; +	case V4L2_SEL_TGT_CROP_BOUNDS: +	case V4L2_SEL_TGT_CROP_DEFAULT: +		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +			return -EINVAL; +		break; +	case V4L2_SEL_TGT_COMPOSE: +		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) +			return -EINVAL; +		use_c_rect = true; +		break; +	case V4L2_SEL_TGT_CROP: +		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) +			return -EINVAL; +		use_c_rect = true; +		break; +	default: +		return -EINVAL; +	} + +	if (use_c_rect) { +		/* +		 * for CROP/COMPOSE target type, return c_rect params from the +		 * respective buffer type +		 */ +		s->r = q_data->c_rect; +	} else { +		/* +		 * for DEFAULT/BOUNDS target type, return width and height from +		 * S_FMT of the respective buffer type +		 */ +		s->r.left = 0; +		s->r.top = 0; +		s->r.width = q_data->width; +		s->r.height = q_data->height; +	} + +	return 0; +} + + +static int vpe_s_selection(struct file *file, void *fh, +		struct v4l2_selection *s) +{ +	struct vpe_ctx *ctx = file2ctx(file); +	struct vpe_q_data *q_data; +	struct v4l2_selection sel = *s; +	int ret; + +	ret = __vpe_try_selection(ctx, &sel); +	if (ret) +		return ret; + +	q_data = get_q_data(ctx, sel.type); +	if (!q_data) +		return -EINVAL; + +	if ((q_data->c_rect.left == sel.r.left) && +			(q_data->c_rect.top == sel.r.top) && +			(q_data->c_rect.width == sel.r.width) && +			(q_data->c_rect.height == sel.r.height)) { +		vpe_dbg(ctx->dev, +			"requested crop/compose values are already set\n"); +		return 0; +	} + +	q_data->c_rect = sel.r; + +	return set_srcdst_params(ctx); +} + +static int vpe_reqbufs(struct file *file, void *priv, +		       struct v4l2_requestbuffers *reqbufs) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) +{ +	struct vpe_ctx *ctx = file2ctx(file); + +	vpe_dump_regs(ctx->dev); +	vpdma_dump_regs(ctx->dev->vpdma); + +	return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +/* + * defines number of buffers/frames a context can process with VPE before + * switching to a different context. default value is 1 buffer per context + */ +#define V4L2_CID_VPE_BUFS_PER_JOB		(V4L2_CID_USER_TI_VPE_BASE + 0) + +static int vpe_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct vpe_ctx *ctx = +		container_of(ctrl->handler, struct vpe_ctx, hdl); + +	switch (ctrl->id) { +	case V4L2_CID_VPE_BUFS_PER_JOB: +		ctx->bufs_per_job = ctrl->val; +		break; + +	default: +		vpe_err(ctx->dev, "Invalid control\n"); +		return -EINVAL; +	} + +	return 0; +} + +static const struct v4l2_ctrl_ops vpe_ctrl_ops = { +	.s_ctrl = vpe_s_ctrl, +}; + +static const struct v4l2_ioctl_ops vpe_ioctl_ops = { +	.vidioc_querycap	= vpe_querycap, + +	.vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt, +	.vidioc_g_fmt_vid_cap_mplane	= vpe_g_fmt, +	.vidioc_try_fmt_vid_cap_mplane	= vpe_try_fmt, +	.vidioc_s_fmt_vid_cap_mplane	= vpe_s_fmt, + +	.vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt, +	.vidioc_g_fmt_vid_out_mplane	= vpe_g_fmt, +	.vidioc_try_fmt_vid_out_mplane	= vpe_try_fmt, +	.vidioc_s_fmt_vid_out_mplane	= vpe_s_fmt, + +	.vidioc_g_selection		= vpe_g_selection, +	.vidioc_s_selection		= vpe_s_selection, + +	.vidioc_reqbufs		= vpe_reqbufs, +	.vidioc_querybuf	= vpe_querybuf, + +	.vidioc_qbuf		= vpe_qbuf, +	.vidioc_dqbuf		= vpe_dqbuf, + +	.vidioc_streamon	= vpe_streamon, +	.vidioc_streamoff	= vpe_streamoff, +	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event, +	.vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Queue operations + */ +static int vpe_queue_setup(struct vb2_queue *vq, +			   const struct v4l2_format *fmt, +			   unsigned int *nbuffers, unsigned int *nplanes, +			   unsigned int sizes[], void *alloc_ctxs[]) +{ +	int i; +	struct vpe_ctx *ctx = vb2_get_drv_priv(vq); +	struct vpe_q_data *q_data; + +	q_data = get_q_data(ctx, vq->type); + +	*nplanes = q_data->fmt->coplanar ? 2 : 1; + +	for (i = 0; i < *nplanes; i++) { +		sizes[i] = q_data->sizeimage[i]; +		alloc_ctxs[i] = ctx->dev->alloc_ctx; +	} + +	vpe_dbg(ctx->dev, "get %d buffer(s) of size %d", *nbuffers, +		sizes[VPE_LUMA]); +	if (q_data->fmt->coplanar) +		vpe_dbg(ctx->dev, " and %d\n", sizes[VPE_CHROMA]); + +	return 0; +} + +static int vpe_buf_prepare(struct vb2_buffer *vb) +{ +	struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); +	struct vpe_q_data *q_data; +	int i, num_planes; + +	vpe_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type); + +	q_data = get_q_data(ctx, vb->vb2_queue->type); +	num_planes = q_data->fmt->coplanar ? 2 : 1; + +	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { +		if (!(q_data->flags & Q_DATA_INTERLACED)) { +			vb->v4l2_buf.field = V4L2_FIELD_NONE; +		} else { +			if (vb->v4l2_buf.field != V4L2_FIELD_TOP && +					vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) +				return -EINVAL; +		} +	} + +	for (i = 0; i < num_planes; i++) { +		if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { +			vpe_err(ctx->dev, +				"data will not fit into plane (%lu < %lu)\n", +				vb2_plane_size(vb, i), +				(long) q_data->sizeimage[i]); +			return -EINVAL; +		} +	} + +	for (i = 0; i < num_planes; i++) +		vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); + +	return 0; +} + +static void vpe_buf_queue(struct vb2_buffer *vb) +{ +	struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); +	v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} + +static void vpe_wait_prepare(struct vb2_queue *q) +{ +	struct vpe_ctx *ctx = vb2_get_drv_priv(q); +	vpe_unlock(ctx); +} + +static void vpe_wait_finish(struct vb2_queue *q) +{ +	struct vpe_ctx *ctx = vb2_get_drv_priv(q); +	vpe_lock(ctx); +} + +static struct vb2_ops vpe_qops = { +	.queue_setup	 = vpe_queue_setup, +	.buf_prepare	 = vpe_buf_prepare, +	.buf_queue	 = vpe_buf_queue, +	.wait_prepare	 = vpe_wait_prepare, +	.wait_finish	 = vpe_wait_finish, +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, +		      struct vb2_queue *dst_vq) +{ +	struct vpe_ctx *ctx = priv; +	int ret; + +	memset(src_vq, 0, sizeof(*src_vq)); +	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; +	src_vq->io_modes = VB2_MMAP | VB2_DMABUF; +	src_vq->drv_priv = ctx; +	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); +	src_vq->ops = &vpe_qops; +	src_vq->mem_ops = &vb2_dma_contig_memops; +	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + +	ret = vb2_queue_init(src_vq); +	if (ret) +		return ret; + +	memset(dst_vq, 0, sizeof(*dst_vq)); +	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; +	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; +	dst_vq->drv_priv = ctx; +	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); +	dst_vq->ops = &vpe_qops; +	dst_vq->mem_ops = &vb2_dma_contig_memops; +	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + +	return vb2_queue_init(dst_vq); +} + +static const struct v4l2_ctrl_config vpe_bufs_per_job = { +	.ops = &vpe_ctrl_ops, +	.id = V4L2_CID_VPE_BUFS_PER_JOB, +	.name = "Buffers Per Transaction", +	.type = V4L2_CTRL_TYPE_INTEGER, +	.def = VPE_DEF_BUFS_PER_JOB, +	.min = 1, +	.max = VIDEO_MAX_FRAME, +	.step = 1, +}; + +/* + * File operations + */ +static int vpe_open(struct file *file) +{ +	struct vpe_dev *dev = video_drvdata(file); +	struct vpe_ctx *ctx = NULL; +	struct vpe_q_data *s_q_data; +	struct v4l2_ctrl_handler *hdl; +	int ret; + +	vpe_dbg(dev, "vpe_open\n"); + +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); +	if (!ctx) +		return -ENOMEM; + +	ctx->dev = dev; + +	if (mutex_lock_interruptible(&dev->dev_mutex)) { +		ret = -ERESTARTSYS; +		goto free_ctx; +	} + +	ret = vpdma_create_desc_list(&ctx->desc_list, VPE_DESC_LIST_SIZE, +			VPDMA_LIST_TYPE_NORMAL); +	if (ret != 0) +		goto unlock; + +	ret = vpdma_alloc_desc_buf(&ctx->mmr_adb, sizeof(struct vpe_mmr_adb)); +	if (ret != 0) +		goto free_desc_list; + +	ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_h, SC_COEF_SRAM_SIZE); +	if (ret != 0) +		goto free_mmr_adb; + +	ret = vpdma_alloc_desc_buf(&ctx->sc_coeff_v, SC_COEF_SRAM_SIZE); +	if (ret != 0) +		goto free_sc_h; + +	init_adb_hdrs(ctx); + +	v4l2_fh_init(&ctx->fh, video_devdata(file)); +	file->private_data = &ctx->fh; + +	hdl = &ctx->hdl; +	v4l2_ctrl_handler_init(hdl, 1); +	v4l2_ctrl_new_custom(hdl, &vpe_bufs_per_job, NULL); +	if (hdl->error) { +		ret = hdl->error; +		goto exit_fh; +	} +	ctx->fh.ctrl_handler = hdl; +	v4l2_ctrl_handler_setup(hdl); + +	s_q_data = &ctx->q_data[Q_DATA_SRC]; +	s_q_data->fmt = &vpe_formats[2]; +	s_q_data->width = 1920; +	s_q_data->height = 1080; +	s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width * +			s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; +	s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] * +			s_q_data->height); +	s_q_data->colorspace = V4L2_COLORSPACE_REC709; +	s_q_data->field = V4L2_FIELD_NONE; +	s_q_data->c_rect.left = 0; +	s_q_data->c_rect.top = 0; +	s_q_data->c_rect.width = s_q_data->width; +	s_q_data->c_rect.height = s_q_data->height; +	s_q_data->flags = 0; + +	ctx->q_data[Q_DATA_DST] = *s_q_data; + +	set_dei_shadow_registers(ctx); +	set_src_registers(ctx); +	set_dst_registers(ctx); +	ret = set_srcdst_params(ctx); +	if (ret) +		goto exit_fh; + +	ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + +	if (IS_ERR(ctx->m2m_ctx)) { +		ret = PTR_ERR(ctx->m2m_ctx); +		goto exit_fh; +	} + +	v4l2_fh_add(&ctx->fh); + +	/* +	 * for now, just report the creation of the first instance, we can later +	 * optimize the driver to enable or disable clocks when the first +	 * instance is created or the last instance released +	 */ +	if (atomic_inc_return(&dev->num_instances) == 1) +		vpe_dbg(dev, "first instance created\n"); + +	ctx->bufs_per_job = VPE_DEF_BUFS_PER_JOB; + +	ctx->load_mmrs = true; + +	vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n", +		ctx, ctx->m2m_ctx); + +	mutex_unlock(&dev->dev_mutex); + +	return 0; +exit_fh: +	v4l2_ctrl_handler_free(hdl); +	v4l2_fh_exit(&ctx->fh); +	vpdma_free_desc_buf(&ctx->sc_coeff_v); +free_sc_h: +	vpdma_free_desc_buf(&ctx->sc_coeff_h); +free_mmr_adb: +	vpdma_free_desc_buf(&ctx->mmr_adb); +free_desc_list: +	vpdma_free_desc_list(&ctx->desc_list); +unlock: +	mutex_unlock(&dev->dev_mutex); +free_ctx: +	kfree(ctx); +	return ret; +} + +static int vpe_release(struct file *file) +{ +	struct vpe_dev *dev = video_drvdata(file); +	struct vpe_ctx *ctx = file2ctx(file); + +	vpe_dbg(dev, "releasing instance %p\n", ctx); + +	mutex_lock(&dev->dev_mutex); +	free_vbs(ctx); +	free_mv_buffers(ctx); +	vpdma_free_desc_list(&ctx->desc_list); +	vpdma_free_desc_buf(&ctx->mmr_adb); + +	v4l2_fh_del(&ctx->fh); +	v4l2_fh_exit(&ctx->fh); +	v4l2_ctrl_handler_free(&ctx->hdl); +	v4l2_m2m_ctx_release(ctx->m2m_ctx); + +	kfree(ctx); + +	/* +	 * for now, just report the release of the last instance, we can later +	 * optimize the driver to enable or disable clocks when the first +	 * instance is created or the last instance released +	 */ +	if (atomic_dec_return(&dev->num_instances) == 0) +		vpe_dbg(dev, "last instance released\n"); + +	mutex_unlock(&dev->dev_mutex); + +	return 0; +} + +static unsigned int vpe_poll(struct file *file, +			     struct poll_table_struct *wait) +{ +	struct vpe_ctx *ctx = file2ctx(file); +	struct vpe_dev *dev = ctx->dev; +	int ret; + +	mutex_lock(&dev->dev_mutex); +	ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +	mutex_unlock(&dev->dev_mutex); +	return ret; +} + +static int vpe_mmap(struct file *file, struct vm_area_struct *vma) +{ +	struct vpe_ctx *ctx = file2ctx(file); +	struct vpe_dev *dev = ctx->dev; +	int ret; + +	if (mutex_lock_interruptible(&dev->dev_mutex)) +		return -ERESTARTSYS; +	ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +	mutex_unlock(&dev->dev_mutex); +	return ret; +} + +static const struct v4l2_file_operations vpe_fops = { +	.owner		= THIS_MODULE, +	.open		= vpe_open, +	.release	= vpe_release, +	.poll		= vpe_poll, +	.unlocked_ioctl	= video_ioctl2, +	.mmap		= vpe_mmap, +}; + +static struct video_device vpe_videodev = { +	.name		= VPE_MODULE_NAME, +	.fops		= &vpe_fops, +	.ioctl_ops	= &vpe_ioctl_ops, +	.minor		= -1, +	.release	= video_device_release_empty, +	.vfl_dir	= VFL_DIR_M2M, +}; + +static struct v4l2_m2m_ops m2m_ops = { +	.device_run	= device_run, +	.job_ready	= job_ready, +	.job_abort	= job_abort, +	.lock		= vpe_lock, +	.unlock		= vpe_unlock, +}; + +static int vpe_runtime_get(struct platform_device *pdev) +{ +	int r; + +	dev_dbg(&pdev->dev, "vpe_runtime_get\n"); + +	r = pm_runtime_get_sync(&pdev->dev); +	WARN_ON(r < 0); +	return r < 0 ? r : 0; +} + +static void vpe_runtime_put(struct platform_device *pdev) +{ + +	int r; + +	dev_dbg(&pdev->dev, "vpe_runtime_put\n"); + +	r = pm_runtime_put_sync(&pdev->dev); +	WARN_ON(r < 0 && r != -ENOSYS); +} + +static void vpe_fw_cb(struct platform_device *pdev) +{ +	struct vpe_dev *dev = platform_get_drvdata(pdev); +	struct video_device *vfd; +	int ret; + +	vfd = &dev->vfd; +	*vfd = vpe_videodev; +	vfd->lock = &dev->dev_mutex; +	vfd->v4l2_dev = &dev->v4l2_dev; + +	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); +	if (ret) { +		vpe_err(dev, "Failed to register video device\n"); + +		vpe_set_clock_enable(dev, 0); +		vpe_runtime_put(pdev); +		pm_runtime_disable(&pdev->dev); +		v4l2_m2m_release(dev->m2m_dev); +		vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); +		v4l2_device_unregister(&dev->v4l2_dev); + +		return; +	} + +	video_set_drvdata(vfd, dev); +	snprintf(vfd->name, sizeof(vfd->name), "%s", vpe_videodev.name); +	dev_info(dev->v4l2_dev.dev, "Device registered as /dev/video%d\n", +		vfd->num); +} + +static int vpe_probe(struct platform_device *pdev) +{ +	struct vpe_dev *dev; +	int ret, irq, func; + +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); +	if (!dev) +		return -ENOMEM; + +	spin_lock_init(&dev->lock); + +	ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); +	if (ret) +		return ret; + +	atomic_set(&dev->num_instances, 0); +	mutex_init(&dev->dev_mutex); + +	dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +			"vpe_top"); +	/* +	 * HACK: we get resource info from device tree in the form of a list of +	 * VPE sub blocks, the driver currently uses only the base of vpe_top +	 * for register access, the driver should be changed later to access +	 * registers based on the sub block base addresses +	 */ +	dev->base = devm_ioremap(&pdev->dev, dev->res->start, SZ_32K); +	if (!dev->base) { +		ret = -ENOMEM; +		goto v4l2_dev_unreg; +	} + +	irq = platform_get_irq(pdev, 0); +	ret = devm_request_irq(&pdev->dev, irq, vpe_irq, 0, VPE_MODULE_NAME, +			dev); +	if (ret) +		goto v4l2_dev_unreg; + +	platform_set_drvdata(pdev, dev); + +	dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); +	if (IS_ERR(dev->alloc_ctx)) { +		vpe_err(dev, "Failed to alloc vb2 context\n"); +		ret = PTR_ERR(dev->alloc_ctx); +		goto v4l2_dev_unreg; +	} + +	dev->m2m_dev = v4l2_m2m_init(&m2m_ops); +	if (IS_ERR(dev->m2m_dev)) { +		vpe_err(dev, "Failed to init mem2mem device\n"); +		ret = PTR_ERR(dev->m2m_dev); +		goto rel_ctx; +	} + +	pm_runtime_enable(&pdev->dev); + +	ret = vpe_runtime_get(pdev); +	if (ret) +		goto rel_m2m; + +	/* Perform clk enable followed by reset */ +	vpe_set_clock_enable(dev, 1); + +	vpe_top_reset(dev); + +	func = read_field_reg(dev, VPE_PID, VPE_PID_FUNC_MASK, +		VPE_PID_FUNC_SHIFT); +	vpe_dbg(dev, "VPE PID function %x\n", func); + +	vpe_top_vpdma_reset(dev); + +	dev->sc = sc_create(pdev); +	if (IS_ERR(dev->sc)) { +		ret = PTR_ERR(dev->sc); +		goto runtime_put; +	} + +	dev->csc = csc_create(pdev); +	if (IS_ERR(dev->csc)) { +		ret = PTR_ERR(dev->csc); +		goto runtime_put; +	} + +	dev->vpdma = vpdma_create(pdev, vpe_fw_cb); +	if (IS_ERR(dev->vpdma)) { +		ret = PTR_ERR(dev->vpdma); +		goto runtime_put; +	} + +	return 0; + +runtime_put: +	vpe_runtime_put(pdev); +rel_m2m: +	pm_runtime_disable(&pdev->dev); +	v4l2_m2m_release(dev->m2m_dev); +rel_ctx: +	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); +v4l2_dev_unreg: +	v4l2_device_unregister(&dev->v4l2_dev); + +	return ret; +} + +static int vpe_remove(struct platform_device *pdev) +{ +	struct vpe_dev *dev = +		(struct vpe_dev *) platform_get_drvdata(pdev); + +	v4l2_info(&dev->v4l2_dev, "Removing " VPE_MODULE_NAME); + +	v4l2_m2m_release(dev->m2m_dev); +	video_unregister_device(&dev->vfd); +	v4l2_device_unregister(&dev->v4l2_dev); +	vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); + +	vpe_set_clock_enable(dev, 0); +	vpe_runtime_put(pdev); +	pm_runtime_disable(&pdev->dev); + +	return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id vpe_of_match[] = { +	{ +		.compatible = "ti,vpe", +	}, +	{}, +}; +#else +#define vpe_of_match NULL +#endif + +static struct platform_driver vpe_pdrv = { +	.probe		= vpe_probe, +	.remove		= vpe_remove, +	.driver		= { +		.name	= VPE_MODULE_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = vpe_of_match, +	}, +}; + +module_platform_driver(vpe_pdrv); + +MODULE_DESCRIPTION("TI VPE driver"); +MODULE_AUTHOR("Dale Farnsworth, <dale@farnsworth.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti-vpe/vpe_regs.h new file mode 100644 index 00000000000..74283d79eae --- /dev/null +++ b/drivers/media/platform/ti-vpe/vpe_regs.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2013 Texas Instruments Inc. + * + * David Griego, <dagriego@biglakesoftware.com> + * Dale Farnsworth, <dale@farnsworth.org> + * Archit Taneja, <archit@ti.com> + * + * 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. + */ + +#ifndef __TI_VPE_REGS_H +#define __TI_VPE_REGS_H + +/* VPE register offsets and field selectors */ + +/* VPE top level regs */ +#define VPE_PID				0x0000 +#define VPE_PID_MINOR_MASK		0x3f +#define VPE_PID_MINOR_SHIFT		0 +#define VPE_PID_CUSTOM_MASK		0x03 +#define VPE_PID_CUSTOM_SHIFT		6 +#define VPE_PID_MAJOR_MASK		0x07 +#define VPE_PID_MAJOR_SHIFT		8 +#define VPE_PID_RTL_MASK		0x1f +#define VPE_PID_RTL_SHIFT		11 +#define VPE_PID_FUNC_MASK		0xfff +#define VPE_PID_FUNC_SHIFT		16 +#define VPE_PID_SCHEME_MASK		0x03 +#define VPE_PID_SCHEME_SHIFT		30 + +#define VPE_SYSCONFIG			0x0010 +#define VPE_SYSCONFIG_IDLE_MASK		0x03 +#define VPE_SYSCONFIG_IDLE_SHIFT	2 +#define VPE_SYSCONFIG_STANDBY_MASK	0x03 +#define VPE_SYSCONFIG_STANDBY_SHIFT	4 +#define VPE_FORCE_IDLE_MODE		0 +#define VPE_NO_IDLE_MODE		1 +#define VPE_SMART_IDLE_MODE		2 +#define VPE_SMART_IDLE_WAKEUP_MODE	3 +#define VPE_FORCE_STANDBY_MODE		0 +#define VPE_NO_STANDBY_MODE		1 +#define VPE_SMART_STANDBY_MODE		2 +#define VPE_SMART_STANDBY_WAKEUP_MODE	3 + +#define VPE_INT0_STATUS0_RAW_SET	0x0020 +#define VPE_INT0_STATUS0_RAW		VPE_INT0_STATUS0_RAW_SET +#define VPE_INT0_STATUS0_CLR		0x0028 +#define VPE_INT0_STATUS0		VPE_INT0_STATUS0_CLR +#define VPE_INT0_ENABLE0_SET		0x0030 +#define VPE_INT0_ENABLE0		VPE_INT0_ENABLE0_SET +#define VPE_INT0_ENABLE0_CLR		0x0038 +#define VPE_INT0_LIST0_COMPLETE		(1 << 0) +#define VPE_INT0_LIST0_NOTIFY		(1 << 1) +#define VPE_INT0_LIST1_COMPLETE		(1 << 2) +#define VPE_INT0_LIST1_NOTIFY		(1 << 3) +#define VPE_INT0_LIST2_COMPLETE		(1 << 4) +#define VPE_INT0_LIST2_NOTIFY		(1 << 5) +#define VPE_INT0_LIST3_COMPLETE		(1 << 6) +#define VPE_INT0_LIST3_NOTIFY		(1 << 7) +#define VPE_INT0_LIST4_COMPLETE		(1 << 8) +#define VPE_INT0_LIST4_NOTIFY		(1 << 9) +#define VPE_INT0_LIST5_COMPLETE		(1 << 10) +#define VPE_INT0_LIST5_NOTIFY		(1 << 11) +#define VPE_INT0_LIST6_COMPLETE		(1 << 12) +#define VPE_INT0_LIST6_NOTIFY		(1 << 13) +#define VPE_INT0_LIST7_COMPLETE		(1 << 14) +#define VPE_INT0_LIST7_NOTIFY		(1 << 15) +#define VPE_INT0_DESCRIPTOR		(1 << 16) +#define VPE_DEI_FMD_INT			(1 << 18) + +#define VPE_INT0_STATUS1_RAW_SET	0x0024 +#define VPE_INT0_STATUS1_RAW		VPE_INT0_STATUS1_RAW_SET +#define VPE_INT0_STATUS1_CLR		0x002c +#define VPE_INT0_STATUS1		VPE_INT0_STATUS1_CLR +#define VPE_INT0_ENABLE1_SET		0x0034 +#define VPE_INT0_ENABLE1		VPE_INT0_ENABLE1_SET +#define VPE_INT0_ENABLE1_CLR		0x003c +#define VPE_INT0_CHANNEL_GROUP0		(1 << 0) +#define VPE_INT0_CHANNEL_GROUP1		(1 << 1) +#define VPE_INT0_CHANNEL_GROUP2		(1 << 2) +#define VPE_INT0_CHANNEL_GROUP3		(1 << 3) +#define VPE_INT0_CHANNEL_GROUP4		(1 << 4) +#define VPE_INT0_CHANNEL_GROUP5		(1 << 5) +#define VPE_INT0_CLIENT			(1 << 7) +#define VPE_DEI_ERROR_INT		(1 << 16) +#define VPE_DS1_UV_ERROR_INT		(1 << 22) + +#define VPE_INTC_EOI			0x00a0 + +#define VPE_CLK_ENABLE			0x0100 +#define VPE_VPEDMA_CLK_ENABLE		(1 << 0) +#define VPE_DATA_PATH_CLK_ENABLE	(1 << 1) + +#define VPE_CLK_RESET			0x0104 +#define VPE_VPDMA_CLK_RESET_MASK	0x1 +#define VPE_VPDMA_CLK_RESET_SHIFT	0 +#define VPE_DATA_PATH_CLK_RESET_MASK	0x1 +#define VPE_DATA_PATH_CLK_RESET_SHIFT	1 +#define VPE_MAIN_RESET_MASK		0x1 +#define VPE_MAIN_RESET_SHIFT		31 + +#define VPE_CLK_FORMAT_SELECT		0x010c +#define VPE_CSC_SRC_SELECT_MASK		0x03 +#define VPE_CSC_SRC_SELECT_SHIFT	0 +#define VPE_RGB_OUT_SELECT		(1 << 8) +#define VPE_DS_SRC_SELECT_MASK		0x07 +#define VPE_DS_SRC_SELECT_SHIFT		9 +#define VPE_DS_BYPASS			(1 << 16) +#define VPE_COLOR_SEPARATE_422		(1 << 18) + +#define VPE_DS_SRC_DEI_SCALER		(5 << VPE_DS_SRC_SELECT_SHIFT) +#define VPE_CSC_SRC_DEI_SCALER		(3 << VPE_CSC_SRC_SELECT_SHIFT) + +#define VPE_CLK_RANGE_MAP		0x011c +#define VPE_RANGE_RANGE_MAP_Y_MASK	0x07 +#define VPE_RANGE_RANGE_MAP_Y_SHIFT	0 +#define VPE_RANGE_RANGE_MAP_UV_MASK	0x07 +#define VPE_RANGE_RANGE_MAP_UV_SHIFT	3 +#define VPE_RANGE_MAP_ON		(1 << 6) +#define VPE_RANGE_REDUCTION_ON		(1 << 28) + +/* VPE chrominance upsampler regs */ +#define VPE_US1_R0			0x0304 +#define VPE_US2_R0			0x0404 +#define VPE_US3_R0			0x0504 +#define VPE_US_C1_MASK			0x3fff +#define VPE_US_C1_SHIFT			2 +#define VPE_US_C0_MASK			0x3fff +#define VPE_US_C0_SHIFT			18 +#define VPE_US_MODE_MASK		0x03 +#define VPE_US_MODE_SHIFT		16 +#define VPE_ANCHOR_FID0_C1_MASK		0x3fff +#define VPE_ANCHOR_FID0_C1_SHIFT	2 +#define VPE_ANCHOR_FID0_C0_MASK		0x3fff +#define VPE_ANCHOR_FID0_C0_SHIFT	18 + +#define VPE_US1_R1			0x0308 +#define VPE_US2_R1			0x0408 +#define VPE_US3_R1			0x0508 +#define VPE_ANCHOR_FID0_C3_MASK		0x3fff +#define VPE_ANCHOR_FID0_C3_SHIFT	2 +#define VPE_ANCHOR_FID0_C2_MASK		0x3fff +#define VPE_ANCHOR_FID0_C2_SHIFT	18 + +#define VPE_US1_R2			0x030c +#define VPE_US2_R2			0x040c +#define VPE_US3_R2			0x050c +#define VPE_INTERP_FID0_C1_MASK		0x3fff +#define VPE_INTERP_FID0_C1_SHIFT	2 +#define VPE_INTERP_FID0_C0_MASK		0x3fff +#define VPE_INTERP_FID0_C0_SHIFT	18 + +#define VPE_US1_R3			0x0310 +#define VPE_US2_R3			0x0410 +#define VPE_US3_R3			0x0510 +#define VPE_INTERP_FID0_C3_MASK		0x3fff +#define VPE_INTERP_FID0_C3_SHIFT	2 +#define VPE_INTERP_FID0_C2_MASK		0x3fff +#define VPE_INTERP_FID0_C2_SHIFT	18 + +#define VPE_US1_R4			0x0314 +#define VPE_US2_R4			0x0414 +#define VPE_US3_R4			0x0514 +#define VPE_ANCHOR_FID1_C1_MASK		0x3fff +#define VPE_ANCHOR_FID1_C1_SHIFT	2 +#define VPE_ANCHOR_FID1_C0_MASK		0x3fff +#define VPE_ANCHOR_FID1_C0_SHIFT	18 + +#define VPE_US1_R5			0x0318 +#define VPE_US2_R5			0x0418 +#define VPE_US3_R5			0x0518 +#define VPE_ANCHOR_FID1_C3_MASK		0x3fff +#define VPE_ANCHOR_FID1_C3_SHIFT	2 +#define VPE_ANCHOR_FID1_C2_MASK		0x3fff +#define VPE_ANCHOR_FID1_C2_SHIFT	18 + +#define VPE_US1_R6			0x031c +#define VPE_US2_R6			0x041c +#define VPE_US3_R6			0x051c +#define VPE_INTERP_FID1_C1_MASK		0x3fff +#define VPE_INTERP_FID1_C1_SHIFT	2 +#define VPE_INTERP_FID1_C0_MASK		0x3fff +#define VPE_INTERP_FID1_C0_SHIFT	18 + +#define VPE_US1_R7			0x0320 +#define VPE_US2_R7			0x0420 +#define VPE_US3_R7			0x0520 +#define VPE_INTERP_FID0_C3_MASK		0x3fff +#define VPE_INTERP_FID0_C3_SHIFT	2 +#define VPE_INTERP_FID0_C2_MASK		0x3fff +#define VPE_INTERP_FID0_C2_SHIFT	18 + +/* VPE de-interlacer regs */ +#define VPE_DEI_FRAME_SIZE		0x0600 +#define VPE_DEI_WIDTH_MASK		0x07ff +#define VPE_DEI_WIDTH_SHIFT		0 +#define VPE_DEI_HEIGHT_MASK		0x07ff +#define VPE_DEI_HEIGHT_SHIFT		16 +#define VPE_DEI_INTERLACE_BYPASS	(1 << 29) +#define VPE_DEI_FIELD_FLUSH		(1 << 30) +#define VPE_DEI_PROGRESSIVE		(1 << 31) + +#define VPE_MDT_BYPASS			0x0604 +#define VPE_MDT_TEMPMAX_BYPASS		(1 << 0) +#define VPE_MDT_SPATMAX_BYPASS		(1 << 1) + +#define VPE_MDT_SF_THRESHOLD		0x0608 +#define VPE_MDT_SF_SC_THR1_MASK		0xff +#define VPE_MDT_SF_SC_THR1_SHIFT	0 +#define VPE_MDT_SF_SC_THR2_MASK		0xff +#define VPE_MDT_SF_SC_THR2_SHIFT	0 +#define VPE_MDT_SF_SC_THR3_MASK		0xff +#define VPE_MDT_SF_SC_THR3_SHIFT	0 + +#define VPE_EDI_CONFIG			0x060c +#define VPE_EDI_INP_MODE_MASK		0x03 +#define VPE_EDI_INP_MODE_SHIFT		0 +#define VPE_EDI_ENABLE_3D		(1 << 2) +#define VPE_EDI_ENABLE_CHROMA_3D	(1 << 3) +#define VPE_EDI_CHROMA3D_COR_THR_MASK	0xff +#define VPE_EDI_CHROMA3D_COR_THR_SHIFT	8 +#define VPE_EDI_DIR_COR_LOWER_THR_MASK	0xff +#define VPE_EDI_DIR_COR_LOWER_THR_SHIFT	16 +#define VPE_EDI_COR_SCALE_FACTOR_MASK	0xff +#define VPE_EDI_COR_SCALE_FACTOR_SHIFT	23 + +#define VPE_DEI_EDI_LUT_R0		0x0610 +#define VPE_EDI_LUT0_MASK		0x1f +#define VPE_EDI_LUT0_SHIFT		0 +#define VPE_EDI_LUT1_MASK		0x1f +#define VPE_EDI_LUT1_SHIFT		8 +#define VPE_EDI_LUT2_MASK		0x1f +#define VPE_EDI_LUT2_SHIFT		16 +#define VPE_EDI_LUT3_MASK		0x1f +#define VPE_EDI_LUT3_SHIFT		24 + +#define VPE_DEI_EDI_LUT_R1		0x0614 +#define VPE_EDI_LUT0_MASK		0x1f +#define VPE_EDI_LUT0_SHIFT		0 +#define VPE_EDI_LUT1_MASK		0x1f +#define VPE_EDI_LUT1_SHIFT		8 +#define VPE_EDI_LUT2_MASK		0x1f +#define VPE_EDI_LUT2_SHIFT		16 +#define VPE_EDI_LUT3_MASK		0x1f +#define VPE_EDI_LUT3_SHIFT		24 + +#define VPE_DEI_EDI_LUT_R2		0x0618 +#define VPE_EDI_LUT4_MASK		0x1f +#define VPE_EDI_LUT4_SHIFT		0 +#define VPE_EDI_LUT5_MASK		0x1f +#define VPE_EDI_LUT5_SHIFT		8 +#define VPE_EDI_LUT6_MASK		0x1f +#define VPE_EDI_LUT6_SHIFT		16 +#define VPE_EDI_LUT7_MASK		0x1f +#define VPE_EDI_LUT7_SHIFT		24 + +#define VPE_DEI_EDI_LUT_R3		0x061c +#define VPE_EDI_LUT8_MASK		0x1f +#define VPE_EDI_LUT8_SHIFT		0 +#define VPE_EDI_LUT9_MASK		0x1f +#define VPE_EDI_LUT9_SHIFT		8 +#define VPE_EDI_LUT10_MASK		0x1f +#define VPE_EDI_LUT10_SHIFT		16 +#define VPE_EDI_LUT11_MASK		0x1f +#define VPE_EDI_LUT11_SHIFT		24 + +#define VPE_DEI_FMD_WINDOW_R0		0x0620 +#define VPE_FMD_WINDOW_MINX_MASK	0x07ff +#define VPE_FMD_WINDOW_MINX_SHIFT	0 +#define VPE_FMD_WINDOW_MAXX_MASK	0x07ff +#define VPE_FMD_WINDOW_MAXX_SHIFT	16 +#define VPE_FMD_WINDOW_ENABLE		(1 << 31) + +#define VPE_DEI_FMD_WINDOW_R1		0x0624 +#define VPE_FMD_WINDOW_MINY_MASK	0x07ff +#define VPE_FMD_WINDOW_MINY_SHIFT	0 +#define VPE_FMD_WINDOW_MAXY_MASK	0x07ff +#define VPE_FMD_WINDOW_MAXY_SHIFT	16 + +#define VPE_DEI_FMD_CONTROL_R0		0x0628 +#define VPE_FMD_ENABLE			(1 << 0) +#define VPE_FMD_LOCK			(1 << 1) +#define VPE_FMD_JAM_DIR			(1 << 2) +#define VPE_FMD_BED_ENABLE		(1 << 3) +#define VPE_FMD_CAF_FIELD_THR_MASK	0xff +#define VPE_FMD_CAF_FIELD_THR_SHIFT	16 +#define VPE_FMD_CAF_LINE_THR_MASK	0xff +#define VPE_FMD_CAF_LINE_THR_SHIFT	24 + +#define VPE_DEI_FMD_CONTROL_R1		0x062c +#define VPE_FMD_CAF_THR_MASK		0x000fffff +#define VPE_FMD_CAF_THR_SHIFT		0 + +#define VPE_DEI_FMD_STATUS_R0		0x0630 +#define VPE_FMD_CAF_MASK		0x000fffff +#define VPE_FMD_CAF_SHIFT		0 +#define VPE_FMD_RESET			(1 << 24) + +#define VPE_DEI_FMD_STATUS_R1		0x0634 +#define VPE_FMD_FIELD_DIFF_MASK		0x0fffffff +#define VPE_FMD_FIELD_DIFF_SHIFT	0 + +#define VPE_DEI_FMD_STATUS_R2		0x0638 +#define VPE_FMD_FRAME_DIFF_MASK		0x000fffff +#define VPE_FMD_FRAME_DIFF_SHIFT	0 + +#endif diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index b557caf5b1a..3cb2f356487 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -347,7 +347,7 @@ static int timblogiw_s_std(struct file *file, void  *priv, v4l2_std_id std)  	mutex_lock(&lw->lock);  	if (TIMBLOGIW_HAS_DECODER(lw)) -		err = v4l2_subdev_call(lw->sd_enc, core, s_std, std); +		err = v4l2_subdev_call(lw->sd_enc, video, s_std, std);  	if (!err)  		fh->cur_norm = timblogiw_get_norm(std); @@ -403,7 +403,7 @@ static int timblogiw_s_input(struct file *file, void  *priv, unsigned int input)  	return 0;  } -static int timblogiw_streamon(struct file *file, void  *priv, unsigned int type) +static int timblogiw_streamon(struct file *file, void  *priv, enum v4l2_buf_type type)  {  	struct video_device *vdev = video_devdata(file);  	struct timblogiw_fh *fh = priv; @@ -420,7 +420,7 @@ static int timblogiw_streamon(struct file *file, void  *priv, unsigned int type)  }  static int timblogiw_streamoff(struct file *file, void  *priv, -	unsigned int type) +	enum v4l2_buf_type type)  {  	struct video_device *vdev = video_devdata(file);  	struct timblogiw_fh *fh = priv; @@ -565,7 +565,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)  	desc = dmaengine_prep_slave_sg(fh->chan,  		buf->sg, sg_elems, DMA_DEV_TO_MEM, -		DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); +		DMA_PREP_INTERRUPT);  	if (!desc) {  		spin_lock_irq(&fh->queue_lock);  		list_del_init(&vb->queue); @@ -800,7 +800,7 @@ static int timblogiw_probe(struct platform_device *pdev)  	if (!pdata->encoder.module_name)  		dev_info(&pdev->dev, "Running without decoder\n"); -	lw = kzalloc(sizeof(*lw), GFP_KERNEL); +	lw = devm_kzalloc(&pdev->dev, sizeof(*lw), GFP_KERNEL);  	if (!lw) {  		err = -ENOMEM;  		goto err; @@ -820,7 +820,7 @@ static int timblogiw_probe(struct platform_device *pdev)  	strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name));  	err = v4l2_device_register(NULL, &lw->v4l2_dev);  	if (err) -		goto err_register; +		goto err;  	lw->video_dev.v4l2_dev = &lw->v4l2_dev; @@ -837,8 +837,6 @@ static int timblogiw_probe(struct platform_device *pdev)  err_request:  	v4l2_device_unregister(&lw->v4l2_dev); -err_register: -	kfree(lw);  err:  	dev_err(&pdev->dev, "Failed to register: %d\n", err); @@ -853,8 +851,6 @@ static int timblogiw_remove(struct platform_device *pdev)  	v4l2_device_unregister(&lw->v4l2_dev); -	kfree(lw); -  	return 0;  } diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index c6af974c5b4..470d3533611 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2586,7 +2586,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)  			}  			if (data_norm == 3)  				data_norm = VINO_DATA_NORM_PAL; -			ret = decoder_call(core, s_std, norm); +			ret = decoder_call(video, s_std, norm);  		}  		spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2675,7 +2675,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)  				}  				if (data_norm == 3)  					data_norm = VINO_DATA_NORM_PAL; -				ret = decoder_call(core, s_std, norm); +				ret = decoder_call(video, s_std, norm);  			}  			spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2809,7 +2809,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs,  		 * as it may take a while... */  		norm = vino_data_norms[data_norm].std; -		err = decoder_call(core, s_std, norm); +		err = decoder_call(video, s_std, norm);  		spin_lock_irqsave(&vino_drvdata->input_lock, *flags); diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 1d3f1196519..d00bf3df0f8 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -70,10 +70,6 @@ static unsigned debug;  module_param(debug, uint, 0644);  MODULE_PARM_DESC(debug, "activates debug info"); -static unsigned int vid_limit = 16; -module_param(vid_limit, uint, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); -  /* Global font descriptor */  static const u8 *font8x16; @@ -191,7 +187,6 @@ struct vivi_buffer {  	/* common v4l buffer stuff -- must be first */  	struct vb2_buffer	vb;  	struct list_head	list; -	const struct vivi_fmt  *fmt;  };  struct vivi_dmaqueue { @@ -254,7 +249,7 @@ struct vivi_dev {  	struct v4l2_fract          timeperframe;  	unsigned int               width, height;  	struct vb2_queue	   vb_vidq; -	unsigned int		   field_count; +	unsigned int		   seq_count;  	u8			   bars[9][3];  	u8			   line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); @@ -675,8 +670,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)  	dev->mv_count += 2;  	buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; -	dev->field_count++; -	buf->vb.v4l2_buf.sequence = dev->field_count >> 1; +	buf->vb.v4l2_buf.sequence = dev->seq_count++;  	v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);  } @@ -818,19 +812,15 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,  	struct vivi_dev *dev = vb2_get_drv_priv(vq);  	unsigned long size; -	if (fmt) +	size = dev->width * dev->height * dev->pixelsize; +	if (fmt) { +		if (fmt->fmt.pix.sizeimage < size) +			return -EINVAL;  		size = fmt->fmt.pix.sizeimage; -	else -		size = dev->width * dev->height * dev->pixelsize; - -	if (size == 0) -		return -EINVAL; - -	if (0 == *nbuffers) -		*nbuffers = 32; - -	while (size * *nbuffers > vid_limit * 1024 * 1024) -		(*nbuffers)--; +		/* check against insane over 8K resolution buffers */ +		if (size > 7680 * 4320 * dev->pixelsize) +			return -EINVAL; +	}  	*nplanes = 1; @@ -876,8 +866,6 @@ static int buffer_prepare(struct vb2_buffer *vb)  	vb2_set_plane_payload(&buf->vb, 0, size); -	buf->fmt = dev->fmt; -  	precalculate_bars(dev);  	precalculate_line(dev); @@ -901,17 +889,28 @@ static void buffer_queue(struct vb2_buffer *vb)  static int start_streaming(struct vb2_queue *vq, unsigned int count)  {  	struct vivi_dev *dev = vb2_get_drv_priv(vq); +	int err; +  	dprintk(dev, 1, "%s\n", __func__); -	return vivi_start_generating(dev); +	dev->seq_count = 0; +	err = vivi_start_generating(dev); +	if (err) { +		struct vivi_buffer *buf, *tmp; + +		list_for_each_entry_safe(buf, tmp, &dev->vidq.active, list) { +			list_del(&buf->list); +			vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); +		} +	} +	return err;  }  /* abort streaming and wait for last buffer */ -static int stop_streaming(struct vb2_queue *vq) +static void stop_streaming(struct vb2_queue *vq)  {  	struct vivi_dev *dev = vb2_get_drv_priv(vq);  	dprintk(dev, 1, "%s\n", __func__);  	vivi_stop_generating(dev); -	return 0;  }  static void vivi_lock(struct vb2_queue *vq) @@ -1108,7 +1107,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)  	return 0;  } -/* timeperframe is arbitrary and continous */ +/* timeperframe is arbitrary and continuous */  static int vidioc_enum_frameintervals(struct file *file, void *priv,  					     struct v4l2_frmivalenum *fival)  { @@ -1121,11 +1120,15 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv,  	if (!fmt)  		return -EINVAL; -	/* regarding width & height - we support any */ +	/* check for valid width/height */ +	if (fival->width < 48 || fival->width > MAX_WIDTH || (fival->width & 3)) +		return -EINVAL; +	if (fival->height < 32 || fival->height > MAX_HEIGHT) +		return -EINVAL;  	fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; -	/* fill in stepwise (step=1.0 is requred by V4L2 spec) */ +	/* fill in stepwise (step=1.0 is required by V4L2 spec) */  	fival->stepwise.min  = tpf_min;  	fival->stepwise.max  = tpf_max;  	fival->stepwise.step = (struct v4l2_fract) {1, 1}; @@ -1439,7 +1442,7 @@ static int __init vivi_create_instance(int inst)  	q->buf_struct_size = sizeof(struct vivi_buffer);  	q->ops = &vivi_video_qops;  	q->mem_ops = &vb2_vmalloc_memops; -	q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; +	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;  	ret = vb2_queue_init(q);  	if (ret) diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 4da226169e1..6a93f928dfd 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,5 +1,6 @@  vsp1-y					:= vsp1_drv.o vsp1_entity.o vsp1_video.o  vsp1-y					+= vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o -vsp1-y					+= vsp1_lif.o vsp1_uds.o +vsp1-y					+= vsp1_hsit.o vsp1_lif.o vsp1_lut.o +vsp1-y					+= vsp1_bru.o vsp1_sru.o vsp1_uds.o  obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index d6c6ecd039f..6ca2cf20d54 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -1,7 +1,7 @@  /*   * vsp1.h  --  R-Car VSP1 Driver   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -28,8 +28,12 @@ struct clk;  struct device;  struct vsp1_platform_data; +struct vsp1_bru; +struct vsp1_hsit;  struct vsp1_lif; +struct vsp1_lut;  struct vsp1_rwpf; +struct vsp1_sru;  struct vsp1_uds;  #define VPS1_MAX_RPF		5 @@ -42,13 +46,17 @@ struct vsp1_device {  	void __iomem *mmio;  	struct clk *clock; -	struct clk *rt_clock;  	struct mutex lock;  	int ref_count; +	struct vsp1_bru *bru; +	struct vsp1_hsit *hsi; +	struct vsp1_hsit *hst;  	struct vsp1_lif *lif; +	struct vsp1_lut *lut;  	struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; +	struct vsp1_sru *sru;  	struct vsp1_uds *uds[VPS1_MAX_UDS];  	struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c new file mode 100644 index 00000000000..f8069548006 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -0,0 +1,395 @@ +/* + * vsp1_bru.c  --  R-Car VSP1 Blend ROP Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_bru.h" + +#define BRU_MIN_SIZE				4U +#define BRU_MAX_SIZE				8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg) +{ +	return vsp1_read(bru->entity.vsp1, reg); +} + +static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data) +{ +	vsp1_write(bru->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static bool bru_is_input_enabled(struct vsp1_bru *bru, unsigned int input) +{ +	return media_entity_remote_pad(&bru->entity.pads[input]) != NULL; +} + +static int bru_s_stream(struct v4l2_subdev *subdev, int enable) +{ +	struct vsp1_bru *bru = to_bru(subdev); +	struct v4l2_mbus_framefmt *format; +	unsigned int i; + +	if (!enable) +		return 0; + +	format = &bru->entity.formats[BRU_PAD_SOURCE]; + +	/* The hardware is extremely flexible but we have no userspace API to +	 * expose all the parameters, nor is it clear whether we would have use +	 * cases for all the supported modes. Let's just harcode the parameters +	 * to sane default values for now. +	 */ + +	/* Disable both color data normalization and dithering. */ +	vsp1_bru_write(bru, VI6_BRU_INCTRL, 0); + +	/* Set the background position to cover the whole output image and +	 * set its color to opaque black. +	 */ +	vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE, +		       (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | +		       (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); +	vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0); +	vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, +		       0xff << VI6_BRU_VIRRPF_COL_A_SHIFT); + +	/* Route BRU input 1 as SRC input to the ROP unit and configure the ROP +	 * unit with a NOP operation to make BRU input 1 available as the +	 * Blend/ROP unit B SRC input. +	 */ +	vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | +		       VI6_BRU_ROP_CROP(VI6_ROP_NOP) | +		       VI6_BRU_ROP_AROP(VI6_ROP_NOP)); + +	for (i = 0; i < 4; ++i) { +		u32 ctrl = 0; + +		/* Configure all Blend/ROP units corresponding to an enabled BRU +		 * input for alpha blending. Blend/ROP units corresponding to +		 * disabled BRU inputs are used in ROP NOP mode to ignore the +		 * SRC input. +		 */ +		if (bru_is_input_enabled(bru, i)) +			ctrl |= VI6_BRU_CTRL_RBC; +		else +			ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) +			     |  VI6_BRU_CTRL_AROP(VI6_ROP_NOP); + +		/* Select the virtual RPF as the Blend/ROP unit A DST input to +		 * serve as a background color. +		 */ +		if (i == 0) +			ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; + +		/* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to +		 * D in that order. The Blend/ROP unit B SRC is hardwired to the +		 * ROP unit output, the corresponding register bits must be set +		 * to 0. +		 */ +		if (i != 1) +			ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); + +		vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl); + +		/* Harcode the blending formula to +		 * +		 *	DSTc = DSTc * (1 - SRCa) + SRCc * SRCa +		 *	DSTa = DSTa * (1 - SRCa) + SRCa +		 */ +		vsp1_bru_write(bru, VI6_BRU_BLD(i), +			       VI6_BRU_BLD_CCMDX_255_SRC_A | +			       VI6_BRU_BLD_CCMDY_SRC_A | +			       VI6_BRU_BLD_ACMDX_255_SRC_A | +			       VI6_BRU_BLD_ACMDY_COEFY | +			       (0xff << VI6_BRU_BLD_COEFY_SHIFT)); +	} + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +/* + * The BRU can't perform format conversion, all sink and source formats must be + * identical. We pick the format on the first sink pad (pad 0) and propagate it + * to all other pads. + */ + +static int bru_enum_mbus_code(struct v4l2_subdev *subdev, +			      struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_mbus_code_enum *code) +{ +	static const unsigned int codes[] = { +		V4L2_MBUS_FMT_ARGB8888_1X32, +		V4L2_MBUS_FMT_AYUV8_1X32, +	}; +	struct v4l2_mbus_framefmt *format; + +	if (code->pad == BRU_PAD_SINK(0)) { +		if (code->index >= ARRAY_SIZE(codes)) +			return -EINVAL; + +		code->code = codes[code->index]; +	} else { +		if (code->index) +			return -EINVAL; + +		format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0)); +		code->code = format->code; +	} + +	return 0; +} + +static int bru_enum_frame_size(struct v4l2_subdev *subdev, +			       struct v4l2_subdev_fh *fh, +			       struct v4l2_subdev_frame_size_enum *fse) +{ +	if (fse->index) +		return -EINVAL; + +	if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 && +	    fse->code != V4L2_MBUS_FMT_AYUV8_1X32) +		return -EINVAL; + +	fse->min_width = BRU_MIN_SIZE; +	fse->max_width = BRU_MAX_SIZE; +	fse->min_height = BRU_MIN_SIZE; +	fse->max_height = BRU_MAX_SIZE; + +	return 0; +} + +static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, +					 struct v4l2_subdev_fh *fh, +					 unsigned int pad, u32 which) +{ +	switch (which) { +	case V4L2_SUBDEV_FORMAT_TRY: +		return v4l2_subdev_get_try_crop(fh, pad); +	case V4L2_SUBDEV_FORMAT_ACTIVE: +		return &bru->compose[pad]; +	default: +		return NULL; +	} +} + +static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_bru *bru = to_bru(subdev); + +	fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, +						  fmt->which); + +	return 0; +} + +static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh, +			   unsigned int pad, struct v4l2_mbus_framefmt *fmt, +			   enum v4l2_subdev_format_whence which) +{ +	struct v4l2_mbus_framefmt *format; + +	switch (pad) { +	case BRU_PAD_SINK(0): +		/* Default to YUV if the requested format is not supported. */ +		if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && +		    fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) +			fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; +		break; + +	default: +		/* The BRU can't perform format conversion. */ +		format = vsp1_entity_get_pad_format(&bru->entity, fh, +						    BRU_PAD_SINK(0), which); +		fmt->code = format->code; +		break; +	} + +	fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE); +	fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE); +	fmt->field = V4L2_FIELD_NONE; +	fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_bru *bru = to_bru(subdev); +	struct v4l2_mbus_framefmt *format; + +	bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which); + +	format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad, +					    fmt->which); +	*format = fmt->format; + +	/* Reset the compose rectangle */ +	if (fmt->pad != BRU_PAD_SOURCE) { +		struct v4l2_rect *compose; + +		compose = bru_get_compose(bru, fh, fmt->pad, fmt->which); +		compose->left = 0; +		compose->top = 0; +		compose->width = format->width; +		compose->height = format->height; +	} + +	/* Propagate the format code to all pads */ +	if (fmt->pad == BRU_PAD_SINK(0)) { +		unsigned int i; + +		for (i = 0; i <= BRU_PAD_SOURCE; ++i) { +			format = vsp1_entity_get_pad_format(&bru->entity, fh, +							    i, fmt->which); +			format->code = fmt->format.code; +		} +	} + +	return 0; +} + +static int bru_get_selection(struct v4l2_subdev *subdev, +			     struct v4l2_subdev_fh *fh, +			     struct v4l2_subdev_selection *sel) +{ +	struct vsp1_bru *bru = to_bru(subdev); + +	if (sel->pad == BRU_PAD_SOURCE) +		return -EINVAL; + +	switch (sel->target) { +	case V4L2_SEL_TGT_COMPOSE_BOUNDS: +		sel->r.left = 0; +		sel->r.top = 0; +		sel->r.width = BRU_MAX_SIZE; +		sel->r.height = BRU_MAX_SIZE; +		return 0; + +	case V4L2_SEL_TGT_COMPOSE: +		sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which); +		return 0; + +	default: +		return -EINVAL; +	} +} + +static int bru_set_selection(struct v4l2_subdev *subdev, +			     struct v4l2_subdev_fh *fh, +			     struct v4l2_subdev_selection *sel) +{ +	struct vsp1_bru *bru = to_bru(subdev); +	struct v4l2_mbus_framefmt *format; +	struct v4l2_rect *compose; + +	if (sel->pad == BRU_PAD_SOURCE) +		return -EINVAL; + +	if (sel->target != V4L2_SEL_TGT_COMPOSE) +		return -EINVAL; + +	/* The compose rectangle top left corner must be inside the output +	 * frame. +	 */ +	format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE, +					    sel->which); +	sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); +	sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); + +	/* Scaling isn't supported, the compose rectangle size must be identical +	 * to the sink format size. +	 */ +	format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad, +					    sel->which); +	sel->r.width = format->width; +	sel->r.height = format->height; + +	compose = bru_get_compose(bru, fh, sel->pad, sel->which); +	*compose = sel->r; + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops bru_video_ops = { +	.s_stream = bru_s_stream, +}; + +static struct v4l2_subdev_pad_ops bru_pad_ops = { +	.enum_mbus_code = bru_enum_mbus_code, +	.enum_frame_size = bru_enum_frame_size, +	.get_fmt = bru_get_format, +	.set_fmt = bru_set_format, +	.get_selection = bru_get_selection, +	.set_selection = bru_set_selection, +}; + +static struct v4l2_subdev_ops bru_ops = { +	.video	= &bru_video_ops, +	.pad    = &bru_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1) +{ +	struct v4l2_subdev *subdev; +	struct vsp1_bru *bru; +	int ret; + +	bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL); +	if (bru == NULL) +		return ERR_PTR(-ENOMEM); + +	bru->entity.type = VSP1_ENTITY_BRU; + +	ret = vsp1_entity_init(vsp1, &bru->entity, 5); +	if (ret < 0) +		return ERR_PTR(ret); + +	/* Initialize the V4L2 subdev. */ +	subdev = &bru->entity.subdev; +	v4l2_subdev_init(subdev, &bru_ops); + +	subdev->entity.ops = &vsp1_media_ops; +	subdev->internal_ops = &vsp1_subdev_internal_ops; +	snprintf(subdev->name, sizeof(subdev->name), "%s bru", +		 dev_name(vsp1->dev)); +	v4l2_set_subdevdata(subdev, bru); +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	vsp1_entity_init_formats(subdev, NULL); + +	return bru; +} diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h new file mode 100644 index 00000000000..37062704dbf --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_bru.h @@ -0,0 +1,39 @@ +/* + * vsp1_bru.h  --  R-Car VSP1 Blend ROP Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_BRU_H__ +#define __VSP1_BRU_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define BRU_PAD_SINK(n)				(n) +#define BRU_PAD_SOURCE				4 + +struct vsp1_bru { +	struct vsp1_entity entity; + +	struct v4l2_rect compose[4]; +}; + +static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) +{ +	return container_of(subdev, struct vsp1_bru, entity.subdev); +} + +struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_BRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 1c9e771aa15..c69ee0657f7 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -1,7 +1,7 @@  /*   * vsp1_drv.c  --  R-Car VSP1 Driver   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -16,12 +16,17 @@  #include <linux/device.h>  #include <linux/interrupt.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/platform_device.h>  #include <linux/videodev2.h>  #include "vsp1.h" +#include "vsp1_bru.h" +#include "vsp1_hsit.h"  #include "vsp1_lif.h" +#include "vsp1_lut.h"  #include "vsp1_rwpf.h" +#include "vsp1_sru.h"  #include "vsp1_uds.h"  /* ----------------------------------------------------------------------------- @@ -152,6 +157,30 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)  	}  	/* Instantiate all the entities. */ +	vsp1->bru = vsp1_bru_create(vsp1); +	if (IS_ERR(vsp1->bru)) { +		ret = PTR_ERR(vsp1->bru); +		goto done; +	} + +	list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities); + +	vsp1->hsi = vsp1_hsit_create(vsp1, true); +	if (IS_ERR(vsp1->hsi)) { +		ret = PTR_ERR(vsp1->hsi); +		goto done; +	} + +	list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); + +	vsp1->hst = vsp1_hsit_create(vsp1, false); +	if (IS_ERR(vsp1->hst)) { +		ret = PTR_ERR(vsp1->hst); +		goto done; +	} + +	list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); +  	if (vsp1->pdata->features & VSP1_HAS_LIF) {  		vsp1->lif = vsp1_lif_create(vsp1);  		if (IS_ERR(vsp1->lif)) { @@ -162,6 +191,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)  		list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);  	} +	if (vsp1->pdata->features & VSP1_HAS_LUT) { +		vsp1->lut = vsp1_lut_create(vsp1); +		if (IS_ERR(vsp1->lut)) { +			ret = PTR_ERR(vsp1->lut); +			goto done; +		} + +		list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); +	} +  	for (i = 0; i < vsp1->pdata->rpf_count; ++i) {  		struct vsp1_rwpf *rpf; @@ -175,6 +214,16 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)  		list_add_tail(&rpf->entity.list_dev, &vsp1->entities);  	} +	if (vsp1->pdata->features & VSP1_HAS_SRU) { +		vsp1->sru = vsp1_sru_create(vsp1); +		if (IS_ERR(vsp1->sru)) { +			ret = PTR_ERR(vsp1->sru); +			goto done; +		} + +		list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); +	} +  	for (i = 0; i < vsp1->pdata->uds_count; ++i) {  		struct vsp1_uds *uds; @@ -290,40 +339,13 @@ static int vsp1_device_init(struct vsp1_device *vsp1)  	return 0;  } -static int vsp1_clocks_enable(struct vsp1_device *vsp1) -{ -	int ret; - -	ret = clk_prepare_enable(vsp1->clock); -	if (ret < 0) -		return ret; - -	if (IS_ERR(vsp1->rt_clock)) -		return 0; - -	ret = clk_prepare_enable(vsp1->rt_clock); -	if (ret < 0) { -		clk_disable_unprepare(vsp1->clock); -		return ret; -	} - -	return 0; -} - -static void vsp1_clocks_disable(struct vsp1_device *vsp1) -{ -	if (!IS_ERR(vsp1->rt_clock)) -		clk_disable_unprepare(vsp1->rt_clock); -	clk_disable_unprepare(vsp1->clock); -} -  /*   * vsp1_device_get - Acquire the VSP1 device   *   * Increment the VSP1 reference count and initialize the device if the first   * reference is taken.   * - * Return a pointer to the VSP1 device or NULL if an error occured. + * Return a pointer to the VSP1 device or NULL if an error occurred.   */  struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)  { @@ -334,7 +356,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)  	if (vsp1->ref_count > 0)  		goto done; -	ret = vsp1_clocks_enable(vsp1); +	ret = clk_prepare_enable(vsp1->clock);  	if (ret < 0) {  		__vsp1 = NULL;  		goto done; @@ -342,7 +364,7 @@ struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)  	ret = vsp1_device_init(vsp1);  	if (ret < 0) { -		vsp1_clocks_disable(vsp1); +		clk_disable_unprepare(vsp1->clock);  		__vsp1 = NULL;  		goto done;  	} @@ -366,7 +388,7 @@ void vsp1_device_put(struct vsp1_device *vsp1)  	mutex_lock(&vsp1->lock);  	if (--vsp1->ref_count == 0) -		vsp1_clocks_disable(vsp1); +		clk_disable_unprepare(vsp1->clock);  	mutex_unlock(&vsp1->lock);  } @@ -385,7 +407,7 @@ static int vsp1_pm_suspend(struct device *dev)  	if (vsp1->ref_count == 0)  		return 0; -	vsp1_clocks_disable(vsp1); +	clk_disable_unprepare(vsp1->clock);  	return 0;  } @@ -398,7 +420,7 @@ static int vsp1_pm_resume(struct device *dev)  	if (vsp1->ref_count)  		return 0; -	return vsp1_clocks_enable(vsp1); +	return clk_prepare_enable(vsp1->clock);  }  #endif @@ -410,34 +432,59 @@ static const struct dev_pm_ops vsp1_pm_ops = {   * Platform Driver   */ -static struct vsp1_platform_data * -vsp1_get_platform_data(struct platform_device *pdev) +static int vsp1_validate_platform_data(struct platform_device *pdev, +				       struct vsp1_platform_data *pdata)  { -	struct vsp1_platform_data *pdata = pdev->dev.platform_data; -  	if (pdata == NULL) {  		dev_err(&pdev->dev, "missing platform data\n"); -		return NULL; +		return -EINVAL;  	}  	if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) {  		dev_err(&pdev->dev, "invalid number of RPF (%u)\n",  			pdata->rpf_count); -		return NULL; +		return -EINVAL;  	}  	if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) {  		dev_err(&pdev->dev, "invalid number of UDS (%u)\n",  			pdata->uds_count); -		return NULL; +		return -EINVAL;  	}  	if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) {  		dev_err(&pdev->dev, "invalid number of WPF (%u)\n",  			pdata->wpf_count); -		return NULL; +		return -EINVAL;  	} +	return 0; +} + +static struct vsp1_platform_data * +vsp1_get_platform_data(struct platform_device *pdev) +{ +	struct device_node *np = pdev->dev.of_node; +	struct vsp1_platform_data *pdata; + +	if (!IS_ENABLED(CONFIG_OF) || np == NULL) +		return pdev->dev.platform_data; + +	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); +	if (pdata == NULL) +		return NULL; + +	if (of_property_read_bool(np, "renesas,has-lif")) +		pdata->features |= VSP1_HAS_LIF; +	if (of_property_read_bool(np, "renesas,has-lut")) +		pdata->features |= VSP1_HAS_LUT; +	if (of_property_read_bool(np, "renesas,has-sru")) +		pdata->features |= VSP1_HAS_SRU; + +	of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); +	of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); +	of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); +  	return pdata;  } @@ -460,6 +507,10 @@ static int vsp1_probe(struct platform_device *pdev)  	if (vsp1->pdata == NULL)  		return -ENODEV; +	ret = vsp1_validate_platform_data(pdev, vsp1->pdata); +	if (ret < 0) +		return ret; +  	/* I/O, IRQ and clock resources */  	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); @@ -472,9 +523,6 @@ static int vsp1_probe(struct platform_device *pdev)  		return PTR_ERR(vsp1->clock);  	} -	/* The RT clock is optional */ -	vsp1->rt_clock = devm_clk_get(&pdev->dev, "rt"); -  	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  	if (!irq) {  		dev_err(&pdev->dev, "missing IRQ\n"); @@ -509,6 +557,11 @@ static int vsp1_remove(struct platform_device *pdev)  	return 0;  } +static const struct of_device_id vsp1_of_match[] = { +	{ .compatible = "renesas,vsp1" }, +	{ }, +}; +  static struct platform_driver vsp1_platform_driver = {  	.probe		= vsp1_probe,  	.remove		= vsp1_remove, @@ -516,6 +569,7 @@ static struct platform_driver vsp1_platform_driver = {  		.owner	= THIS_MODULE,  		.name	= "vsp1",  		.pm	= &vsp1_pm_ops, +		.of_match_table = vsp1_of_match,  	},  }; diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 9028f9d524f..44167834285 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -1,7 +1,7 @@  /*   * vsp1_entity.c  --  R-Car VSP1 Base Entity   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -15,6 +15,7 @@  #include <linux/gfp.h>  #include <media/media-entity.h> +#include <media/v4l2-ctrls.h>  #include <media/v4l2-subdev.h>  #include "vsp1.h" @@ -99,8 +100,10 @@ static int vsp1_entity_link_setup(struct media_entity *entity,  		if (source->sink)  			return -EBUSY;  		source->sink = remote->entity; +		source->sink_pad = remote->index;  	} else {  		source->sink = NULL; +		source->sink_pad = 0;  	}  	return 0; @@ -115,38 +118,43 @@ const struct media_entity_operations vsp1_media_ops = {   * Initialization   */ +static const struct vsp1_route vsp1_routes[] = { +	{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, +	  { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), +	    VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } }, +	{ VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } }, +	{ VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } }, +	{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } }, +	{ VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } }, +	{ VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } }, +	{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } }, +	{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } }, +	{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } }, +	{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } }, +	{ VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } }, +	{ VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } }, +	{ VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } }, +	{ VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } }, +	{ VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } }, +	{ VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } }, +	{ VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } }, +	{ VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } }, +}; +  int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,  		     unsigned int num_pads)  { -	static const struct { -		unsigned int id; -		unsigned int reg; -	} routes[] = { -		{ VI6_DPR_NODE_LIF, 0 }, -		{ VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) }, -		{ VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, -		{ VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) }, -		{ VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) }, -		{ VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) }, -		{ VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) }, -		{ VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) }, -		{ VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) }, -		{ VI6_DPR_NODE_WPF(0), 0 }, -		{ VI6_DPR_NODE_WPF(1), 0 }, -		{ VI6_DPR_NODE_WPF(2), 0 }, -		{ VI6_DPR_NODE_WPF(3), 0 }, -	}; -  	unsigned int i; -	for (i = 0; i < ARRAY_SIZE(routes); ++i) { -		if (routes[i].id == entity->id) { -			entity->route = routes[i].reg; +	for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) { +		if (vsp1_routes[i].type == entity->type && +		    vsp1_routes[i].index == entity->index) { +			entity->route = &vsp1_routes[i];  			break;  		}  	} -	if (i == ARRAY_SIZE(routes)) +	if (i == ARRAY_SIZE(vsp1_routes))  		return -EINVAL;  	entity->vsp1 = vsp1; @@ -177,5 +185,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,  void vsp1_entity_destroy(struct vsp1_entity *entity)  { +	if (entity->subdev.ctrl_handler) +		v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);  	media_entity_cleanup(&entity->subdev.entity);  } diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index c4feab2cbb8..7afbd8a7ba6 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -1,7 +1,7 @@  /*   * vsp1_entity.h  --  R-Car VSP1 Base Entity   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -20,19 +20,42 @@  struct vsp1_device;  enum vsp1_entity_type { +	VSP1_ENTITY_BRU, +	VSP1_ENTITY_HSI, +	VSP1_ENTITY_HST,  	VSP1_ENTITY_LIF, +	VSP1_ENTITY_LUT,  	VSP1_ENTITY_RPF, +	VSP1_ENTITY_SRU,  	VSP1_ENTITY_UDS,  	VSP1_ENTITY_WPF,  }; +/* + * struct vsp1_route - Entity routing configuration + * @type: Entity type this routing entry is associated with + * @index: Entity index this routing entry is associated with + * @reg: Output routing configuration register + * @inputs: Target node value for each input + * + * Each $vsp1_route entry describes routing configuration for the entity + * specified by the entry's @type and @index. @reg indicates the register that + * holds output routing configuration for the entity, and the @inputs array + * store the target node value for each input of the entity. + */ +struct vsp1_route { +	enum vsp1_entity_type type; +	unsigned int index; +	unsigned int reg; +	unsigned int inputs[4]; +}; +  struct vsp1_entity {  	struct vsp1_device *vsp1;  	enum vsp1_entity_type type;  	unsigned int index; -	unsigned int id; -	unsigned int route; +	const struct vsp1_route *route;  	struct list_head list_dev;  	struct list_head list_pipe; @@ -41,6 +64,7 @@ struct vsp1_entity {  	unsigned int source_pad;  	struct media_entity *sink; +	unsigned int sink_pad;  	struct v4l2_subdev subdev;  	struct v4l2_mbus_framefmt *formats; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c new file mode 100644 index 00000000000..db2950a73c6 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -0,0 +1,219 @@ +/* + * vsp1_hsit.c  --  R-Car VSP1 Hue Saturation value (Inverse) Transform + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_hsit.h" + +#define HSIT_MIN_SIZE				4U +#define HSIT_MAX_SIZE				8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg) +{ +	return vsp1_read(hsit->entity.vsp1, reg); +} + +static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) +{ +	vsp1_write(hsit->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int hsit_s_stream(struct v4l2_subdev *subdev, int enable) +{ +	struct vsp1_hsit *hsit = to_hsit(subdev); + +	if (!enable) +		return 0; + +	if (hsit->inverse) +		vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); +	else +		vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN); + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int hsit_enum_mbus_code(struct v4l2_subdev *subdev, +			       struct v4l2_subdev_fh *fh, +			       struct v4l2_subdev_mbus_code_enum *code) +{ +	struct vsp1_hsit *hsit = to_hsit(subdev); + +	if (code->index > 0) +		return -EINVAL; + +	if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) | +	    (code->pad == HSIT_PAD_SOURCE && hsit->inverse)) +		code->code = V4L2_MBUS_FMT_ARGB8888_1X32; +	else +		code->code = V4L2_MBUS_FMT_AHSV8888_1X32; + +	return 0; +} + +static int hsit_enum_frame_size(struct v4l2_subdev *subdev, +				struct v4l2_subdev_fh *fh, +				struct v4l2_subdev_frame_size_enum *fse) +{ +	struct v4l2_mbus_framefmt *format; + +	format = v4l2_subdev_get_try_format(fh, fse->pad); + +	if (fse->index || fse->code != format->code) +		return -EINVAL; + +	if (fse->pad == HSIT_PAD_SINK) { +		fse->min_width = HSIT_MIN_SIZE; +		fse->max_width = HSIT_MAX_SIZE; +		fse->min_height = HSIT_MIN_SIZE; +		fse->max_height = HSIT_MAX_SIZE; +	} else { +		/* The size on the source pad are fixed and always identical to +		 * the size on the sink pad. +		 */ +		fse->min_width = format->width; +		fse->max_width = format->width; +		fse->min_height = format->height; +		fse->max_height = format->height; +	} + +	return 0; +} + +static int hsit_get_format(struct v4l2_subdev *subdev, +			   struct v4l2_subdev_fh *fh, +			   struct v4l2_subdev_format *fmt) +{ +	struct vsp1_hsit *hsit = to_hsit(subdev); + +	fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, +						  fmt->which); + +	return 0; +} + +static int hsit_set_format(struct v4l2_subdev *subdev, +			   struct v4l2_subdev_fh *fh, +			   struct v4l2_subdev_format *fmt) +{ +	struct vsp1_hsit *hsit = to_hsit(subdev); +	struct v4l2_mbus_framefmt *format; + +	format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad, +					    fmt->which); + +	if (fmt->pad == HSIT_PAD_SOURCE) { +		/* The HST and HSI output format code and resolution can't be +		 * modified. +		 */ +		fmt->format = *format; +		return 0; +	} + +	format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32 +		     : V4L2_MBUS_FMT_ARGB8888_1X32; +	format->width = clamp_t(unsigned int, fmt->format.width, +				HSIT_MIN_SIZE, HSIT_MAX_SIZE); +	format->height = clamp_t(unsigned int, fmt->format.height, +				 HSIT_MIN_SIZE, HSIT_MAX_SIZE); +	format->field = V4L2_FIELD_NONE; +	format->colorspace = V4L2_COLORSPACE_SRGB; + +	fmt->format = *format; + +	/* Propagate the format to the source pad. */ +	format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE, +					    fmt->which); +	*format = fmt->format; +	format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32 +		     : V4L2_MBUS_FMT_AHSV8888_1X32; + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops hsit_video_ops = { +	.s_stream = hsit_s_stream, +}; + +static struct v4l2_subdev_pad_ops hsit_pad_ops = { +	.enum_mbus_code = hsit_enum_mbus_code, +	.enum_frame_size = hsit_enum_frame_size, +	.get_fmt = hsit_get_format, +	.set_fmt = hsit_set_format, +}; + +static struct v4l2_subdev_ops hsit_ops = { +	.video	= &hsit_video_ops, +	.pad    = &hsit_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse) +{ +	struct v4l2_subdev *subdev; +	struct vsp1_hsit *hsit; +	int ret; + +	hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL); +	if (hsit == NULL) +		return ERR_PTR(-ENOMEM); + +	hsit->inverse = inverse; + +	if (inverse) +		hsit->entity.type = VSP1_ENTITY_HSI; +	else +		hsit->entity.type = VSP1_ENTITY_HST; + +	ret = vsp1_entity_init(vsp1, &hsit->entity, 2); +	if (ret < 0) +		return ERR_PTR(ret); + +	/* Initialize the V4L2 subdev. */ +	subdev = &hsit->entity.subdev; +	v4l2_subdev_init(subdev, &hsit_ops); + +	subdev->entity.ops = &vsp1_media_ops; +	subdev->internal_ops = &vsp1_subdev_internal_ops; +	snprintf(subdev->name, sizeof(subdev->name), "%s %s", +		 dev_name(vsp1->dev), inverse ? "hsi" : "hst"); +	v4l2_set_subdevdata(subdev, hsit); +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	vsp1_entity_init_formats(subdev, NULL); + +	return hsit; +} diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h new file mode 100644 index 00000000000..82f1c842690 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_hsit.h @@ -0,0 +1,38 @@ +/* + * vsp1_hsit.h  --  R-Car VSP1 Hue Saturation value (Inverse) Transform + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_HSIT_H__ +#define __VSP1_HSIT_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define HSIT_PAD_SINK				0 +#define HSIT_PAD_SOURCE				1 + +struct vsp1_hsit { +	struct vsp1_entity entity; +	bool inverse; +}; + +static inline struct vsp1_hsit *to_hsit(struct v4l2_subdev *subdev) +{ +	return container_of(subdev, struct vsp1_hsit, entity.subdev); +} + +struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse); + +#endif /* __VSP1_HSIT_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 74a32e69ef1..d4fb23e9c4a 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -1,7 +1,7 @@  /*   * vsp1_lif.c  --  R-Car VSP1 LCD Controller Interface   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -215,7 +215,6 @@ struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)  		return ERR_PTR(-ENOMEM);  	lif->entity.type = VSP1_ENTITY_LIF; -	lif->entity.id = VI6_DPR_NODE_LIF;  	ret = vsp1_entity_init(vsp1, &lif->entity, 2);  	if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h index 89b93af56fd..7b35879028d 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.h +++ b/drivers/media/platform/vsp1/vsp1_lif.h @@ -1,7 +1,7 @@  /*   * vsp1_lif.h  --  R-Car VSP1 LCD Controller Interface   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c new file mode 100644 index 00000000000..fea36ebe256 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -0,0 +1,251 @@ +/* + * vsp1_lut.c  --  R-Car VSP1 Look-Up Table + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/vsp1.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_lut.h" + +#define LUT_MIN_SIZE				4U +#define LUT_MAX_SIZE				8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg) +{ +	return vsp1_read(lut->entity.vsp1, reg); +} + +static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data) +{ +	vsp1_write(lut->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config) +{ +	memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut, +		    sizeof(config->lut)); +} + +static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg) +{ +	struct vsp1_lut *lut = to_lut(subdev); + +	switch (cmd) { +	case VIDIOC_VSP1_LUT_CONFIG: +		lut_configure(lut, arg); +		return 0; + +	default: +		return -ENOIOCTLCMD; +	} +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Video Operations + */ + +static int lut_s_stream(struct v4l2_subdev *subdev, int enable) +{ +	struct vsp1_lut *lut = to_lut(subdev); + +	if (!enable) +		return 0; + +	vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int lut_enum_mbus_code(struct v4l2_subdev *subdev, +			      struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_mbus_code_enum *code) +{ +	static const unsigned int codes[] = { +		V4L2_MBUS_FMT_ARGB8888_1X32, +		V4L2_MBUS_FMT_AHSV8888_1X32, +		V4L2_MBUS_FMT_AYUV8_1X32, +	}; +	struct v4l2_mbus_framefmt *format; + +	if (code->pad == LUT_PAD_SINK) { +		if (code->index >= ARRAY_SIZE(codes)) +			return -EINVAL; + +		code->code = codes[code->index]; +	} else { +		/* The LUT can't perform format conversion, the sink format is +		 * always identical to the source format. +		 */ +		if (code->index) +			return -EINVAL; + +		format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK); +		code->code = format->code; +	} + +	return 0; +} + +static int lut_enum_frame_size(struct v4l2_subdev *subdev, +			       struct v4l2_subdev_fh *fh, +			       struct v4l2_subdev_frame_size_enum *fse) +{ +	struct v4l2_mbus_framefmt *format; + +	format = v4l2_subdev_get_try_format(fh, fse->pad); + +	if (fse->index || fse->code != format->code) +		return -EINVAL; + +	if (fse->pad == LUT_PAD_SINK) { +		fse->min_width = LUT_MIN_SIZE; +		fse->max_width = LUT_MAX_SIZE; +		fse->min_height = LUT_MIN_SIZE; +		fse->max_height = LUT_MAX_SIZE; +	} else { +		/* The size on the source pad are fixed and always identical to +		 * the size on the sink pad. +		 */ +		fse->min_width = format->width; +		fse->max_width = format->width; +		fse->min_height = format->height; +		fse->max_height = format->height; +	} + +	return 0; +} + +static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_lut *lut = to_lut(subdev); + +	fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, +						  fmt->which); + +	return 0; +} + +static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_lut *lut = to_lut(subdev); +	struct v4l2_mbus_framefmt *format; + +	/* Default to YUV if the requested format is not supported. */ +	if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && +	    fmt->format.code != V4L2_MBUS_FMT_AHSV8888_1X32 && +	    fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) +		fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + +	format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad, +					    fmt->which); + +	if (fmt->pad == LUT_PAD_SOURCE) { +		/* The LUT output format can't be modified. */ +		fmt->format = *format; +		return 0; +	} + +	format->width = clamp_t(unsigned int, fmt->format.width, +				LUT_MIN_SIZE, LUT_MAX_SIZE); +	format->height = clamp_t(unsigned int, fmt->format.height, +				 LUT_MIN_SIZE, LUT_MAX_SIZE); +	format->field = V4L2_FIELD_NONE; +	format->colorspace = V4L2_COLORSPACE_SRGB; + +	fmt->format = *format; + +	/* Propagate the format to the source pad. */ +	format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE, +					    fmt->which); +	*format = fmt->format; + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_core_ops lut_core_ops = { +	.ioctl = lut_ioctl, +}; + +static struct v4l2_subdev_video_ops lut_video_ops = { +	.s_stream = lut_s_stream, +}; + +static struct v4l2_subdev_pad_ops lut_pad_ops = { +	.enum_mbus_code = lut_enum_mbus_code, +	.enum_frame_size = lut_enum_frame_size, +	.get_fmt = lut_get_format, +	.set_fmt = lut_set_format, +}; + +static struct v4l2_subdev_ops lut_ops = { +	.core	= &lut_core_ops, +	.video	= &lut_video_ops, +	.pad    = &lut_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) +{ +	struct v4l2_subdev *subdev; +	struct vsp1_lut *lut; +	int ret; + +	lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL); +	if (lut == NULL) +		return ERR_PTR(-ENOMEM); + +	lut->entity.type = VSP1_ENTITY_LUT; + +	ret = vsp1_entity_init(vsp1, &lut->entity, 2); +	if (ret < 0) +		return ERR_PTR(ret); + +	/* Initialize the V4L2 subdev. */ +	subdev = &lut->entity.subdev; +	v4l2_subdev_init(subdev, &lut_ops); + +	subdev->entity.ops = &vsp1_media_ops; +	subdev->internal_ops = &vsp1_subdev_internal_ops; +	snprintf(subdev->name, sizeof(subdev->name), "%s lut", +		 dev_name(vsp1->dev)); +	v4l2_set_subdevdata(subdev, lut); +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	vsp1_entity_init_formats(subdev, NULL); + +	return lut; +} diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h new file mode 100644 index 00000000000..f92ffb86735 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -0,0 +1,38 @@ +/* + * vsp1_lut.h  --  R-Car VSP1 Look-Up Table + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_LUT_H__ +#define __VSP1_LUT_H__ + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define LUT_PAD_SINK				0 +#define LUT_PAD_SOURCE				1 + +struct vsp1_lut { +	struct vsp1_entity entity; +	u32 lut[256]; +}; + +static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev) +{ +	return container_of(subdev, struct vsp1_lut, entity.subdev); +} + +struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_LUT_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 1d3304f1365..3e74b44286f 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -336,8 +336,21 @@   */  #define VI6_SRU_CTRL0			0x2200 +#define VI6_SRU_CTRL0_PARAM0_SHIFT	16 +#define VI6_SRU_CTRL0_PARAM1_SHIFT	8 +#define VI6_SRU_CTRL0_MODE_UPSCALE	(4 << 4) +#define VI6_SRU_CTRL0_PARAM2		(1 << 3) +#define VI6_SRU_CTRL0_PARAM3		(1 << 2) +#define VI6_SRU_CTRL0_PARAM4		(1 << 1) +#define VI6_SRU_CTRL0_EN		(1 << 0) +  #define VI6_SRU_CTRL1			0x2204 +#define VI6_SRU_CTRL1_PARAM5		0x7ff +  #define VI6_SRU_CTRL2			0x2208 +#define VI6_SRU_CTRL2_PARAM6_SHIFT	16 +#define VI6_SRU_CTRL2_PARAM7_SHIFT	8 +#define VI6_SRU_CTRL2_PARAM8_SHIFT	0  /* -----------------------------------------------------------------------------   * UDS Control Registers @@ -412,6 +425,7 @@   */  #define VI6_LUT_CTRL			0x2800 +#define VI6_LUT_CTRL_EN			(1 << 0)  /* -----------------------------------------------------------------------------   * CLU Control Registers @@ -424,24 +438,124 @@   */  #define VI6_HST_CTRL			0x2a00 +#define VI6_HST_CTRL_EN			(1 << 0)  /* -----------------------------------------------------------------------------   * HSI Control Registers   */  #define VI6_HSI_CTRL			0x2b00 +#define VI6_HSI_CTRL_EN			(1 << 0)  /* -----------------------------------------------------------------------------   * BRU Control Registers   */ +#define VI6_ROP_NOP			0 +#define VI6_ROP_AND			1 +#define VI6_ROP_AND_REV			2 +#define VI6_ROP_COPY			3 +#define VI6_ROP_AND_INV			4 +#define VI6_ROP_CLEAR			5 +#define VI6_ROP_XOR			6 +#define VI6_ROP_OR			7 +#define VI6_ROP_NOR			8 +#define VI6_ROP_EQUIV			9 +#define VI6_ROP_INVERT			10 +#define VI6_ROP_OR_REV			11 +#define VI6_ROP_COPY_INV		12 +#define VI6_ROP_OR_INV			13 +#define VI6_ROP_NAND			14 +#define VI6_ROP_SET			15 +  #define VI6_BRU_INCTRL			0x2c00 +#define VI6_BRU_INCTRL_NRM		(1 << 28) +#define VI6_BRU_INCTRL_DnON		(1 << (16 + (n))) +#define VI6_BRU_INCTRL_DITHn_OFF	(0 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_18BPP	(1 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_16BPP	(2 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_15BPP	(3 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_12BPP	(4 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_8BPP	(5 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_MASK	(7 << ((n) * 4)) +#define VI6_BRU_INCTRL_DITHn_SHIFT	((n) * 4) +  #define VI6_BRU_VIRRPF_SIZE		0x2c04 +#define VI6_BRU_VIRRPF_SIZE_HSIZE_MASK	(0x1fff << 16) +#define VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT	16 +#define VI6_BRU_VIRRPF_SIZE_VSIZE_MASK	(0x1fff << 0) +#define VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT	0 +  #define VI6_BRU_VIRRPF_LOC		0x2c08 +#define VI6_BRU_VIRRPF_LOC_HCOORD_MASK	(0x1fff << 16) +#define VI6_BRU_VIRRPF_LOC_HCOORD_SHIFT	16 +#define VI6_BRU_VIRRPF_LOC_VCOORD_MASK	(0x1fff << 0) +#define VI6_BRU_VIRRPF_LOC_VCOORD_SHIFT	0 +  #define VI6_BRU_VIRRPF_COL		0x2c0c +#define VI6_BRU_VIRRPF_COL_A_MASK	(0xff << 24) +#define VI6_BRU_VIRRPF_COL_A_SHIFT	24 +#define VI6_BRU_VIRRPF_COL_RCR_MASK	(0xff << 16) +#define VI6_BRU_VIRRPF_COL_RCR_SHIFT	16 +#define VI6_BRU_VIRRPF_COL_GY_MASK	(0xff << 8) +#define VI6_BRU_VIRRPF_COL_GY_SHIFT	8 +#define VI6_BRU_VIRRPF_COL_BCB_MASK	(0xff << 0) +#define VI6_BRU_VIRRPF_COL_BCB_SHIFT	0 +  #define VI6_BRU_CTRL(n)			(0x2c10 + (n) * 8) +#define VI6_BRU_CTRL_RBC		(1 << 31) +#define VI6_BRU_CTRL_DSTSEL_BRUIN(n)	((n) << 20) +#define VI6_BRU_CTRL_DSTSEL_VRPF	(4 << 20) +#define VI6_BRU_CTRL_DSTSEL_MASK	(7 << 20) +#define VI6_BRU_CTRL_SRCSEL_BRUIN(n)	((n) << 16) +#define VI6_BRU_CTRL_SRCSEL_VRPF	(4 << 16) +#define VI6_BRU_CTRL_SRCSEL_MASK	(7 << 16) +#define VI6_BRU_CTRL_CROP(rop)		((rop) << 4) +#define VI6_BRU_CTRL_CROP_MASK		(0xf << 4) +#define VI6_BRU_CTRL_AROP(rop)		((rop) << 0) +#define VI6_BRU_CTRL_AROP_MASK		(0xf << 0) +  #define VI6_BRU_BLD(n)			(0x2c14 + (n) * 8) +#define VI6_BRU_BLD_CBES		(1 << 31) +#define VI6_BRU_BLD_CCMDX_DST_A		(0 << 28) +#define VI6_BRU_BLD_CCMDX_255_DST_A	(1 << 28) +#define VI6_BRU_BLD_CCMDX_SRC_A		(2 << 28) +#define VI6_BRU_BLD_CCMDX_255_SRC_A	(3 << 28) +#define VI6_BRU_BLD_CCMDX_COEFX		(4 << 28) +#define VI6_BRU_BLD_CCMDX_MASK		(7 << 28) +#define VI6_BRU_BLD_CCMDY_DST_A		(0 << 24) +#define VI6_BRU_BLD_CCMDY_255_DST_A	(1 << 24) +#define VI6_BRU_BLD_CCMDY_SRC_A		(2 << 24) +#define VI6_BRU_BLD_CCMDY_255_SRC_A	(3 << 24) +#define VI6_BRU_BLD_CCMDY_COEFY		(4 << 24) +#define VI6_BRU_BLD_CCMDY_MASK		(7 << 24) +#define VI6_BRU_BLD_CCMDY_SHIFT		24 +#define VI6_BRU_BLD_ABES		(1 << 23) +#define VI6_BRU_BLD_ACMDX_DST_A		(0 << 20) +#define VI6_BRU_BLD_ACMDX_255_DST_A	(1 << 20) +#define VI6_BRU_BLD_ACMDX_SRC_A		(2 << 20) +#define VI6_BRU_BLD_ACMDX_255_SRC_A	(3 << 20) +#define VI6_BRU_BLD_ACMDX_COEFX		(4 << 20) +#define VI6_BRU_BLD_ACMDX_MASK		(7 << 20) +#define VI6_BRU_BLD_ACMDY_DST_A		(0 << 16) +#define VI6_BRU_BLD_ACMDY_255_DST_A	(1 << 16) +#define VI6_BRU_BLD_ACMDY_SRC_A		(2 << 16) +#define VI6_BRU_BLD_ACMDY_255_SRC_A	(3 << 16) +#define VI6_BRU_BLD_ACMDY_COEFY		(4 << 16) +#define VI6_BRU_BLD_ACMDY_MASK		(7 << 16) +#define VI6_BRU_BLD_COEFX_MASK		(0xff << 8) +#define VI6_BRU_BLD_COEFX_SHIFT		8 +#define VI6_BRU_BLD_COEFY_MASK		(0xff << 0) +#define VI6_BRU_BLD_COEFY_SHIFT		0 +  #define VI6_BRU_ROP			0x2c30 +#define VI6_BRU_ROP_DSTSEL_BRUIN(n)	((n) << 20) +#define VI6_BRU_ROP_DSTSEL_VRPF		(4 << 20) +#define VI6_BRU_ROP_DSTSEL_MASK		(7 << 20) +#define VI6_BRU_ROP_CROP(rop)		((rop) << 4) +#define VI6_BRU_ROP_CROP_MASK		(0xf << 4) +#define VI6_BRU_ROP_AROP(rop)		((rop) << 0) +#define VI6_BRU_ROP_AROP_MASK		(0xf << 0)  /* -----------------------------------------------------------------------------   * HGO Control Registers diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index 254871d3423..c3d98642a4a 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -1,7 +1,7 @@  /*   * vsp1_rpf.c  --  R-Car VSP1 Read Pixel Formatter   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -47,25 +47,36 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)  	struct vsp1_rwpf *rpf = to_rwpf(subdev);  	const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;  	const struct v4l2_pix_format_mplane *format = &rpf->video.format; +	const struct v4l2_rect *crop = &rpf->crop;  	u32 pstride;  	u32 infmt;  	if (!enable)  		return 0; -	/* Source size and stride. Cropping isn't supported yet. */ +	/* Source size, stride and crop offsets. +	 * +	 * The crop offsets correspond to the location of the crop rectangle top +	 * left corner in the plane buffer. Only two offsets are needed, as +	 * planes 2 and 3 always have identical strides. +	 */  	vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, -		       (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | -		       (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); +		       (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | +		       (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));  	vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, -		       (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | -		       (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); +		       (crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | +		       (crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); +	rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline +			+ crop->left * fmtinfo->bpp[0] / 8;  	pstride = format->plane_fmt[0].bytesperline  		<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; -	if (format->num_planes > 1) +	if (format->num_planes > 1) { +		rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline +				+ crop->left * fmtinfo->bpp[1] / 8;  		pstride |= format->plane_fmt[1].bytesperline  			<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT; +	}  	vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -85,8 +96,10 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)  	vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);  	vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); -	/* Output location. Composing isn't supported yet. */ -	vsp1_rpf_write(rpf, VI6_RPF_LOC, 0); +	/* Output location */ +	vsp1_rpf_write(rpf, VI6_RPF_LOC, +		       (rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) | +		       (rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));  	/* Disable alpha, mask and color key. Set the alpha channel to a fixed  	 * value of 255. @@ -113,6 +126,8 @@ static struct v4l2_subdev_pad_ops rpf_pad_ops = {  	.enum_frame_size = vsp1_rwpf_enum_frame_size,  	.get_fmt = vsp1_rwpf_get_format,  	.set_fmt = vsp1_rwpf_set_format, +	.get_selection = vsp1_rwpf_get_selection, +	.set_selection = vsp1_rwpf_set_selection,  };  static struct v4l2_subdev_ops rpf_ops = { @@ -129,11 +144,14 @@ static void rpf_vdev_queue(struct vsp1_video *video,  {  	struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); -	vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]); +	vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, +		       buf->addr[0] + rpf->offsets[0]);  	if (buf->buf.num_planes > 1) -		vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]); +		vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, +			       buf->addr[1] + rpf->offsets[1]);  	if (buf->buf.num_planes > 2) -		vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]); +		vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, +			       buf->addr[2] + rpf->offsets[1]);  }  static const struct vsp1_video_operations rpf_vdev_ops = { @@ -160,7 +178,6 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)  	rpf->entity.type = VSP1_ENTITY_RPF;  	rpf->entity.index = index; -	rpf->entity.id = VI6_DPR_NODE_RPF(index);  	ret = vsp1_entity_init(vsp1, &rpf->entity, 2);  	if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index 9752d5516ce..ec3dab6a9b9 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -1,7 +1,7 @@  /*   * vsp1_rwpf.c  --  R-Car VSP1 Read and Write Pixel Formatters   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -71,6 +71,19 @@ int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,  	return 0;  } +static struct v4l2_rect * +vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which) +{ +	switch (which) { +	case V4L2_SUBDEV_FORMAT_TRY: +		return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK); +	case V4L2_SUBDEV_FORMAT_ACTIVE: +		return &rwpf->crop; +	default: +		return NULL; +	} +} +  int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  			 struct v4l2_subdev_format *fmt)  { @@ -87,6 +100,7 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  {  	struct vsp1_rwpf *rwpf = to_rwpf(subdev);  	struct v4l2_mbus_framefmt *format; +	struct v4l2_rect *crop;  	/* Default to YUV if the requested format is not supported. */  	if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && @@ -115,6 +129,13 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  	fmt->format = *format; +	/* Update the sink crop rectangle. */ +	crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which); +	crop->left = 0; +	crop->top = 0; +	crop->width = fmt->format.width; +	crop->height = fmt->format.height; +  	/* Propagate the format to the source pad. */  	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,  					    fmt->which); @@ -122,3 +143,78 @@ int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  	return 0;  } + +int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, +			    struct v4l2_subdev_fh *fh, +			    struct v4l2_subdev_selection *sel) +{ +	struct vsp1_rwpf *rwpf = to_rwpf(subdev); +	struct v4l2_mbus_framefmt *format; + +	/* Cropping is implemented on the sink pad. */ +	if (sel->pad != RWPF_PAD_SINK) +		return -EINVAL; + +	switch (sel->target) { +	case V4L2_SEL_TGT_CROP: +		sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which); +		break; + +	case V4L2_SEL_TGT_CROP_BOUNDS: +		format = vsp1_entity_get_pad_format(&rwpf->entity, fh, +						    RWPF_PAD_SINK, sel->which); +		sel->r.left = 0; +		sel->r.top = 0; +		sel->r.width = format->width; +		sel->r.height = format->height; +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, +			    struct v4l2_subdev_fh *fh, +			    struct v4l2_subdev_selection *sel) +{ +	struct vsp1_rwpf *rwpf = to_rwpf(subdev); +	struct v4l2_mbus_framefmt *format; +	struct v4l2_rect *crop; + +	/* Cropping is implemented on the sink pad. */ +	if (sel->pad != RWPF_PAD_SINK) +		return -EINVAL; + +	if (sel->target != V4L2_SEL_TGT_CROP) +		return -EINVAL; + +	/* Make sure the crop rectangle is entirely contained in the image. The +	 * WPF top and left offsets are limited to 255. +	 */ +	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK, +					    sel->which); +	sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); +	sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); +	if (rwpf->entity.type == VSP1_ENTITY_WPF) { +		sel->r.left = min_t(unsigned int, sel->r.left, 255); +		sel->r.top = min_t(unsigned int, sel->r.top, 255); +	} +	sel->r.width = min_t(unsigned int, sel->r.width, +			     format->width - sel->r.left); +	sel->r.height = min_t(unsigned int, sel->r.height, +			      format->height - sel->r.top); + +	crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which); +	*crop = sel->r; + +	/* Propagate the format to the source pad. */ +	format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, +					    sel->which); +	format->width = crop->width; +	format->height = crop->height; + +	return 0; +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index c182d85f36b..b4fb65e5877 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -1,7 +1,7 @@  /*   * vsp1_rwpf.h  --  R-Car VSP1 Read and Write Pixel Formatters   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -29,6 +29,14 @@ struct vsp1_rwpf {  	unsigned int max_width;  	unsigned int max_height; + +	struct { +		unsigned int left; +		unsigned int top; +	} location; +	struct v4l2_rect crop; + +	unsigned int offsets[2];  };  static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) @@ -49,5 +57,11 @@ int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  			 struct v4l2_subdev_format *fmt);  int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,  			 struct v4l2_subdev_format *fmt); +int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, +			    struct v4l2_subdev_fh *fh, +			    struct v4l2_subdev_selection *sel); +int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, +			    struct v4l2_subdev_fh *fh, +			    struct v4l2_subdev_selection *sel);  #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c new file mode 100644 index 00000000000..aa0e04c56f3 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -0,0 +1,355 @@ +/* + * vsp1_sru.c  --  R-Car VSP1 Super Resolution Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/device.h> +#include <linux/gfp.h> + +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_sru.h" + +#define SRU_MIN_SIZE				4U +#define SRU_MAX_SIZE				8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg) +{ +	return vsp1_read(sru->entity.vsp1, reg); +} + +static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data) +{ +	vsp1_write(sru->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * Controls + */ + +#define V4L2_CID_VSP1_SRU_INTENSITY		(V4L2_CID_USER_BASE + 1) + +static int sru_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct vsp1_sru *sru = +		container_of(ctrl->handler, struct vsp1_sru, ctrls); + +	switch (ctrl->id) { +	case V4L2_CID_VSP1_SRU_INTENSITY: +		sru->intensity = ctrl->val; +		break; +	} + +	return 0; +} + +static const struct v4l2_ctrl_ops sru_ctrl_ops = { +	.s_ctrl = sru_s_ctrl, +}; + +static const struct v4l2_ctrl_config sru_intensity_control = { +	.ops = &sru_ctrl_ops, +	.id = V4L2_CID_VSP1_SRU_INTENSITY, +	.name = "Intensity", +	.type = V4L2_CTRL_TYPE_INTEGER, +	.min = 1, +	.max = 6, +	.step = 1, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +struct vsp1_sru_param { +	u32 ctrl0; +	u32 ctrl2; +}; + +#define VI6_SRU_CTRL0_PARAMS(p0, p1)			\ +	(((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) |		\ +	 ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT)) + +#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8)		\ +	(((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) |		\ +	 ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) |		\ +	 ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT)) + +static const struct vsp1_sru_param vsp1_sru_params[] = { +	{ +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255), +	}, { +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255), +	}, { +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255), +	}, { +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255), +	}, { +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255), +	}, { +		.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, +		.ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255), +	}, +}; + +static int sru_s_stream(struct v4l2_subdev *subdev, int enable) +{ +	struct vsp1_sru *sru = to_sru(subdev); +	const struct vsp1_sru_param *param; +	struct v4l2_mbus_framefmt *input; +	struct v4l2_mbus_framefmt *output; +	bool upscale; +	u32 ctrl0; + +	if (!enable) +		return 0; + +	input = &sru->entity.formats[SRU_PAD_SINK]; +	output = &sru->entity.formats[SRU_PAD_SOURCE]; +	upscale = input->width != output->width; +	param = &vsp1_sru_params[sru->intensity]; + +	if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32) +		ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 +		      | VI6_SRU_CTRL0_PARAM4; +	else +		ctrl0 = VI6_SRU_CTRL0_PARAM3; + +	vsp1_sru_write(sru, VI6_SRU_CTRL0, param->ctrl0 | ctrl0 | +		       (upscale ? VI6_SRU_CTRL0_MODE_UPSCALE : 0)); +	vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); +	vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2); + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int sru_enum_mbus_code(struct v4l2_subdev *subdev, +			      struct v4l2_subdev_fh *fh, +			      struct v4l2_subdev_mbus_code_enum *code) +{ +	static const unsigned int codes[] = { +		V4L2_MBUS_FMT_ARGB8888_1X32, +		V4L2_MBUS_FMT_AYUV8_1X32, +	}; +	struct v4l2_mbus_framefmt *format; + +	if (code->pad == SRU_PAD_SINK) { +		if (code->index >= ARRAY_SIZE(codes)) +			return -EINVAL; + +		code->code = codes[code->index]; +	} else { +		/* The SRU can't perform format conversion, the sink format is +		 * always identical to the source format. +		 */ +		if (code->index) +			return -EINVAL; + +		format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); +		code->code = format->code; +	} + +	return 0; +} + +static int sru_enum_frame_size(struct v4l2_subdev *subdev, +			       struct v4l2_subdev_fh *fh, +			       struct v4l2_subdev_frame_size_enum *fse) +{ +	struct v4l2_mbus_framefmt *format; + +	format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK); + +	if (fse->index || fse->code != format->code) +		return -EINVAL; + +	if (fse->pad == SRU_PAD_SINK) { +		fse->min_width = SRU_MIN_SIZE; +		fse->max_width = SRU_MAX_SIZE; +		fse->min_height = SRU_MIN_SIZE; +		fse->max_height = SRU_MAX_SIZE; +	} else { +		fse->min_width = format->width; +		fse->min_height = format->height; +		if (format->width <= SRU_MAX_SIZE / 2 && +		    format->height <= SRU_MAX_SIZE / 2) { +			fse->max_width = format->width * 2; +			fse->max_height = format->height * 2; +		} else { +			fse->max_width = format->width; +			fse->max_height = format->height; +		} +	} + +	return 0; +} + +static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_sru *sru = to_sru(subdev); + +	fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, +						  fmt->which); + +	return 0; +} + +static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh, +			   unsigned int pad, struct v4l2_mbus_framefmt *fmt, +			   enum v4l2_subdev_format_whence which) +{ +	struct v4l2_mbus_framefmt *format; +	unsigned int input_area; +	unsigned int output_area; + +	switch (pad) { +	case SRU_PAD_SINK: +		/* Default to YUV if the requested format is not supported. */ +		if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && +		    fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) +			fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; + +		fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE); +		fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE); +		break; + +	case SRU_PAD_SOURCE: +		/* The SRU can't perform format conversion. */ +		format = vsp1_entity_get_pad_format(&sru->entity, fh, +						    SRU_PAD_SINK, which); +		fmt->code = format->code; + +		/* We can upscale by 2 in both direction, but not independently. +		 * Compare the input and output rectangles areas (avoiding +		 * integer overflows on the output): if the requested output +		 * area is larger than 1.5^2 the input area upscale by two, +		 * otherwise don't scale. +		 */ +		input_area = format->width * format->height; +		output_area = min(fmt->width, SRU_MAX_SIZE) +			    * min(fmt->height, SRU_MAX_SIZE); + +		if (fmt->width <= SRU_MAX_SIZE / 2 && +		    fmt->height <= SRU_MAX_SIZE / 2 && +		    output_area > input_area * 9 / 4) { +			fmt->width = format->width * 2; +			fmt->height = format->height * 2; +		} else { +			fmt->width = format->width; +			fmt->height = format->height; +		} +		break; +	} + +	fmt->field = V4L2_FIELD_NONE; +	fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, +			  struct v4l2_subdev_format *fmt) +{ +	struct vsp1_sru *sru = to_sru(subdev); +	struct v4l2_mbus_framefmt *format; + +	sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which); + +	format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad, +					    fmt->which); +	*format = fmt->format; + +	if (fmt->pad == SRU_PAD_SINK) { +		/* Propagate the format to the source pad. */ +		format = vsp1_entity_get_pad_format(&sru->entity, fh, +						    SRU_PAD_SOURCE, fmt->which); +		*format = fmt->format; + +		sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which); +	} + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops sru_video_ops = { +	.s_stream = sru_s_stream, +}; + +static struct v4l2_subdev_pad_ops sru_pad_ops = { +	.enum_mbus_code = sru_enum_mbus_code, +	.enum_frame_size = sru_enum_frame_size, +	.get_fmt = sru_get_format, +	.set_fmt = sru_set_format, +}; + +static struct v4l2_subdev_ops sru_ops = { +	.video	= &sru_video_ops, +	.pad    = &sru_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) +{ +	struct v4l2_subdev *subdev; +	struct vsp1_sru *sru; +	int ret; + +	sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL); +	if (sru == NULL) +		return ERR_PTR(-ENOMEM); + +	sru->entity.type = VSP1_ENTITY_SRU; + +	ret = vsp1_entity_init(vsp1, &sru->entity, 2); +	if (ret < 0) +		return ERR_PTR(ret); + +	/* Initialize the V4L2 subdev. */ +	subdev = &sru->entity.subdev; +	v4l2_subdev_init(subdev, &sru_ops); + +	subdev->entity.ops = &vsp1_media_ops; +	subdev->internal_ops = &vsp1_subdev_internal_ops; +	snprintf(subdev->name, sizeof(subdev->name), "%s sru", +		 dev_name(vsp1->dev)); +	v4l2_set_subdevdata(subdev, sru); +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	vsp1_entity_init_formats(subdev, NULL); + +	/* Initialize the control handler. */ +	v4l2_ctrl_handler_init(&sru->ctrls, 1); +	v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); +	v4l2_ctrl_handler_setup(&sru->ctrls); +	sru->entity.subdev.ctrl_handler = &sru->ctrls; + +	return sru; +} diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h new file mode 100644 index 00000000000..381870b7478 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -0,0 +1,41 @@ +/* + * vsp1_sru.h  --  R-Car VSP1 Super Resolution Unit + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_SRU_H__ +#define __VSP1_SRU_H__ + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define SRU_PAD_SINK				0 +#define SRU_PAD_SOURCE				1 + +struct vsp1_sru { +	struct vsp1_entity entity; + +	struct v4l2_ctrl_handler ctrls; +	unsigned int intensity; +}; + +static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev) +{ +	return container_of(subdev, struct vsp1_sru, entity.subdev); +} + +struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_SRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 0e50b37f060..0293bdbb440 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -1,7 +1,7 @@  /*   * vsp1_uds.c  --  R-Car VSP1 Up and Down Scaler   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -131,7 +131,7 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable)  		return 0;  	/* Enable multi-tap scaling. */ -	vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC); +	vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_AON | VI6_UDS_CTRL_BC);  	vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,  		       (uds_passband_width(uds->hscale) @@ -139,7 +139,6 @@ static int uds_s_stream(struct v4l2_subdev *subdev, int enable)  		       (uds_passband_width(uds->vscale)  				<< VI6_UDS_PASS_BWIDTH_V_SHIFT)); -  	/* Set the scaling ratios and the output size. */  	format = &uds->entity.formats[UDS_PAD_SOURCE]; @@ -323,7 +322,6 @@ struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)  	uds->entity.type = VSP1_ENTITY_UDS;  	uds->entity.index = index; -	uds->entity.id = VI6_DPR_NODE_UDS(index);  	ret = vsp1_entity_init(vsp1, &uds->entity, 2);  	if (ret < 0) diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 972a285abdb..479d12df118 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -1,7 +1,7 @@  /*   * vsp1_uds.h  --  R-Car VSP1 Up and Down Scaler   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 714c53ef6c1..8a1253e51f0 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1,7 +1,7 @@  /*   * vsp1_video.c  --  R-Car VSP1 Video Node   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -28,6 +28,7 @@  #include <media/videobuf2-dma-contig.h>  #include "vsp1.h" +#include "vsp1_bru.h"  #include "vsp1_entity.h"  #include "vsp1_rwpf.h"  #include "vsp1_video.h" @@ -280,6 +281,9 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,  	struct media_pad *pad;  	bool uds_found = false; +	input->location.left = 0; +	input->location.top = 0; +  	pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);  	while (1) { @@ -292,6 +296,17 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,  		entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); +		/* A BRU is present in the pipeline, store the compose rectangle +		 * location in the input RPF for use when configuring the RPF. +		 */ +		if (entity->type == VSP1_ENTITY_BRU) { +			struct vsp1_bru *bru = to_bru(&entity->subdev); +			struct v4l2_rect *rect = &bru->compose[pad->index]; + +			input->location.left = rect->left; +			input->location.top = rect->top; +		} +  		/* We've reached the WPF, we're done. */  		if (entity->type == VSP1_ENTITY_WPF)  			break; @@ -363,6 +378,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,  			rwpf->video.pipe_index = 0;  		} else if (e->type == VSP1_ENTITY_LIF) {  			pipe->lif = e; +		} else if (e->type == VSP1_ENTITY_BRU) { +			pipe->bru = e;  		}  	} @@ -392,6 +409,7 @@ error:  	pipe->num_video = 0;  	pipe->num_inputs = 0;  	pipe->output = NULL; +	pipe->bru = NULL;  	pipe->lif = NULL;  	return ret;  } @@ -430,6 +448,7 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)  		pipe->num_video = 0;  		pipe->num_inputs = 0;  		pipe->output = NULL; +		pipe->bru = NULL;  		pipe->lif = NULL;  	} @@ -461,7 +480,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)  	list_for_each_entry(entity, &pipe->entities, list_pipe) {  		if (entity->route) -			vsp1_write(entity->vsp1, entity->route, +			vsp1_write(entity->vsp1, entity->route->reg,  				   VI6_DPR_NODE_UNUSED);  		v4l2_subdev_call(&entity->subdev, video, s_stream, 0); @@ -488,11 +507,17 @@ static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)   * This function completes the current buffer by filling its sequence number,   * time stamp and payload size, and hands it back to the videobuf core.   * + * When operating in DU output mode (deep pipeline to the DU through the LIF), + * the VSP1 needs to constantly supply frames to the display. In that case, if + * no other buffer is queued, reuse the one that has just been processed instead + * of handing it back to the videobuf core. + *   * Return the next queued buffer or NULL if the queue is empty.   */  static struct vsp1_video_buffer *  vsp1_video_complete_buffer(struct vsp1_video *video)  { +	struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);  	struct vsp1_video_buffer *next = NULL;  	struct vsp1_video_buffer *done;  	unsigned long flags; @@ -507,6 +532,13 @@ vsp1_video_complete_buffer(struct vsp1_video *video)  	done = list_first_entry(&video->irqqueue,  				struct vsp1_video_buffer, queue); + +	/* In DU output mode reuse the buffer if the list is singular. */ +	if (pipe->lif && list_is_singular(&video->irqqueue)) { +		spin_unlock_irqrestore(&video->irqlock, flags); +		return done; +	} +  	list_del(&done->queue);  	if (!list_empty(&video->irqqueue)) @@ -667,11 +699,12 @@ static void vsp1_entity_route_setup(struct vsp1_entity *source)  {  	struct vsp1_entity *sink; -	if (source->route == 0) +	if (source->route->reg == 0)  		return;  	sink = container_of(source->sink, struct vsp1_entity, subdev.entity); -	vsp1_write(source->vsp1, source->route, sink->id); +	vsp1_write(source->vsp1, source->route->reg, +		   sink->route->inputs[source->sink_pad]);  }  static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -707,7 +740,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)  	return 0;  } -static int vsp1_video_stop_streaming(struct vb2_queue *vq) +static void vsp1_video_stop_streaming(struct vb2_queue *vq)  {  	struct vsp1_video *video = vb2_get_drv_priv(vq);  	struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); @@ -730,8 +763,6 @@ static int vsp1_video_stop_streaming(struct vb2_queue *vq)  	spin_lock_irqsave(&video->irqlock, flags);  	INIT_LIST_HEAD(&video->irqqueue);  	spin_unlock_irqrestore(&video->irqlock, flags); - -	return 0;  }  static struct vb2_ops vsp1_video_queue_qops = { @@ -1026,8 +1057,10 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)  	/* ... and the buffers queue... */  	video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev); -	if (IS_ERR(video->alloc_ctx)) +	if (IS_ERR(video->alloc_ctx)) { +		ret = PTR_ERR(video->alloc_ctx);  		goto error; +	}  	video->queue.type = video->type;  	video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; @@ -1036,7 +1069,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)  	video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer);  	video->queue.ops = &vsp1_video_queue_qops;  	video->queue.mem_ops = &vb2_dma_contig_memops; -	video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; +	video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;  	ret = vb2_queue_init(&video->queue);  	if (ret < 0) {  		dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index d8612a37834..c04d48fa299 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -1,7 +1,7 @@  /*   * vsp1_video.h  --  R-Car VSP1 Video Node   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -75,6 +75,7 @@ struct vsp1_pipeline {  	unsigned int num_inputs;  	struct vsp1_rwpf *inputs[VPS1_MAX_RPF];  	struct vsp1_rwpf *output; +	struct vsp1_entity *bru;  	struct vsp1_entity *lif;  	struct list_head entities; diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index db4b85ee05f..1294340dcb3 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -1,7 +1,7 @@  /*   * vsp1_wpf.c  --  R-Car VSP1 Write Pixel Formatter   * - * Copyright (C) 2013 Renesas Corporation + * Copyright (C) 2013-2014 Renesas Electronics Corporation   *   * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)   * @@ -48,8 +48,7 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)  	struct vsp1_pipeline *pipe =  		to_vsp1_pipeline(&wpf->entity.subdev.entity);  	struct vsp1_device *vsp1 = wpf->entity.vsp1; -	const struct v4l2_mbus_framefmt *format = -		&wpf->entity.formats[RWPF_PAD_SOURCE]; +	const struct v4l2_rect *crop = &wpf->crop;  	unsigned int i;  	u32 srcrpf = 0;  	u32 outfmt = 0; @@ -59,16 +58,24 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)  		return 0;  	} -	/* Sources */ +	/* Sources. If the pipeline has a single input configure it as the +	 * master layer. Otherwise configure all inputs as sub-layers and +	 * select the virtual RPF as the master layer. +	 */  	for (i = 0; i < pipe->num_inputs; ++i) {  		struct vsp1_rwpf *input = pipe->inputs[i]; -		srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index); +		srcrpf |= pipe->num_inputs == 1 +			? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) +			: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);  	} +	if (pipe->num_inputs > 1) +		srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; +  	vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); -	/* Destination stride. Cropping isn't supported yet. */ +	/* Destination stride. */  	if (!pipe->lif) {  		struct v4l2_pix_format_mplane *format = &wpf->video.format; @@ -79,10 +86,12 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)  				       format->plane_fmt[1].bytesperline);  	} -	vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, -		       format->width << VI6_WPF_SZCLIP_SIZE_SHIFT); -	vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, -		       format->height << VI6_WPF_SZCLIP_SIZE_SHIFT); +	vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | +		       (crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) | +		       (crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT)); +	vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | +		       (crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) | +		       (crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT));  	/* Format */  	if (!pipe->lif) { @@ -130,6 +139,8 @@ static struct v4l2_subdev_pad_ops wpf_pad_ops = {  	.enum_frame_size = vsp1_rwpf_enum_frame_size,  	.get_fmt = vsp1_rwpf_get_format,  	.set_fmt = vsp1_rwpf_set_format, +	.get_selection = vsp1_rwpf_get_selection, +	.set_selection = vsp1_rwpf_set_selection,  };  static struct v4l2_subdev_ops wpf_ops = { @@ -178,7 +189,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)  	wpf->entity.type = VSP1_ENTITY_WPF;  	wpf->entity.index = index; -	wpf->entity.id = VI6_DPR_NODE_WPF(index);  	ret = vsp1_entity_init(vsp1, &wpf->entity, 2);  	if (ret < 0)  | 
