diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_wpf.c')
| -rw-r--r-- | drivers/media/platform/vsp1/vsp1_wpf.c | 161 | 
1 files changed, 151 insertions, 10 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6c91eaa35e75..31983169c24a 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -37,6 +37,97 @@ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,  }  /* ----------------------------------------------------------------------------- + * Controls + */ + +enum wpf_flip_ctrl { +	WPF_CTRL_VFLIP = 0, +	WPF_CTRL_HFLIP = 1, +	WPF_CTRL_MAX, +}; + +static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) +{ +	struct vsp1_rwpf *wpf = +		container_of(ctrl->handler, struct vsp1_rwpf, ctrls); +	unsigned int i; +	u32 flip = 0; + +	switch (ctrl->id) { +	case V4L2_CID_HFLIP: +	case V4L2_CID_VFLIP: +		for (i = 0; i < WPF_CTRL_MAX; ++i) { +			if (wpf->flip.ctrls[i]) +				flip |= wpf->flip.ctrls[i]->val ? BIT(i) : 0; +		} + +		spin_lock_irq(&wpf->flip.lock); +		wpf->flip.pending = flip; +		spin_unlock_irq(&wpf->flip.lock); +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = { +	.s_ctrl = vsp1_wpf_s_ctrl, +}; + +static int wpf_init_controls(struct vsp1_rwpf *wpf) +{ +	struct vsp1_device *vsp1 = wpf->entity.vsp1; +	unsigned int num_flip_ctrls; + +	spin_lock_init(&wpf->flip.lock); + +	if (wpf->entity.index != 0) { +		/* Only WPF0 supports flipping. */ +		num_flip_ctrls = 0; +	} else if (vsp1->info->features & VSP1_HAS_WPF_HFLIP) { +		/* When horizontal flip is supported the WPF implements two +		 * controls (horizontal flip and vertical flip). +		 */ +		num_flip_ctrls = 2; +	} else if (vsp1->info->features & VSP1_HAS_WPF_VFLIP) { +		/* When only vertical flip is supported the WPF implements a +		 * single control (vertical flip). +		 */ +		num_flip_ctrls = 1; +	} else { +		/* Otherwise flipping is not supported. */ +		num_flip_ctrls = 0; +	} + +	vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + +	if (num_flip_ctrls >= 1) { +		wpf->flip.ctrls[WPF_CTRL_VFLIP] = +			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, +					  V4L2_CID_VFLIP, 0, 1, 1, 0); +	} + +	if (num_flip_ctrls == 2) { +		wpf->flip.ctrls[WPF_CTRL_HFLIP] = +			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, +					  V4L2_CID_HFLIP, 0, 1, 1, 0); + +		v4l2_ctrl_cluster(2, wpf->flip.ctrls); +	} + +	if (wpf->ctrls.error) { +		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", +			wpf->entity.index); +		return wpf->ctrls.error; +	} + +	return 0; +} + +/* -----------------------------------------------------------------------------   * V4L2 Subdevice Core Operations   */ @@ -62,11 +153,11 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)   * V4L2 Subdevice Operations   */ -static struct v4l2_subdev_video_ops wpf_video_ops = { +static const struct v4l2_subdev_video_ops wpf_video_ops = {  	.s_stream = wpf_s_stream,  }; -static struct v4l2_subdev_ops wpf_ops = { +static const struct v4l2_subdev_ops wpf_ops = {  	.video	= &wpf_video_ops,  	.pad    = &vsp1_rwpf_pad_ops,  }; @@ -85,15 +176,37 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity)  static void wpf_set_memory(struct vsp1_entity *entity, struct vsp1_dl_list *dl)  {  	struct vsp1_rwpf *wpf = entity_to_rwpf(entity); +	const struct v4l2_pix_format_mplane *format = &wpf->format; +	struct vsp1_rwpf_memory mem = wpf->mem; +	unsigned int flip = wpf->flip.active; +	unsigned int offset; + +	/* Update the memory offsets based on flipping configuration. The +	 * destination addresses point to the locations where the VSP starts +	 * writing to memory, which can be different corners of the image +	 * depending on vertical flipping. Horizontal flipping is handled +	 * through a line buffer and doesn't modify the start address. +	 */ +	if (flip & BIT(WPF_CTRL_VFLIP)) { +		mem.addr[0] += (format->height - 1) +			     * format->plane_fmt[0].bytesperline; + +		if (format->num_planes > 1) { +			offset = (format->height / wpf->fmtinfo->vsub - 1) +			       * format->plane_fmt[1].bytesperline; +			mem.addr[1] += offset; +			mem.addr[2] += offset; +		} +	} -	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, wpf->mem.addr[0]); -	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, wpf->mem.addr[1]); -	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, wpf->mem.addr[2]); +	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); +	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); +	vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);  }  static void wpf_configure(struct vsp1_entity *entity,  			  struct vsp1_pipeline *pipe, -			  struct vsp1_dl_list *dl) +			  struct vsp1_dl_list *dl, bool full)  {  	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);  	struct vsp1_device *vsp1 = wpf->entity.vsp1; @@ -104,6 +217,26 @@ static void wpf_configure(struct vsp1_entity *entity,  	u32 outfmt = 0;  	u32 srcrpf = 0; +	if (!full) { +		const unsigned int mask = BIT(WPF_CTRL_VFLIP) +					| BIT(WPF_CTRL_HFLIP); + +		spin_lock(&wpf->flip.lock); +		wpf->flip.active = (wpf->flip.active & ~mask) +				 | (wpf->flip.pending & mask); +		spin_unlock(&wpf->flip.lock); + +		outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; + +		if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) +			outfmt |= VI6_WPF_OUTFMT_FLP; +		if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) +			outfmt |= VI6_WPF_OUTFMT_HFLP; + +		vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); +		return; +	} +  	/* Cropping */  	crop = vsp1_rwpf_get_crop(wpf, wpf->entity.config); @@ -143,13 +276,18 @@ static void wpf_configure(struct vsp1_entity *entity,  				       format->plane_fmt[1].bytesperline);  		vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); + +		if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && +		    wpf->entity.index == 0) +			vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, +				       VI6_WPF_ROT_CTRL_LN16 | +				       (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));  	}  	if (sink_format->code != source_format->code)  		outfmt |= VI6_WPF_OUTFMT_CSC; -	outfmt |= wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT; -	vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); +	wpf->outfmt = outfmt;  	vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index),  			   VI6_DPR_WPF_FPORCH_FP_WPFN); @@ -216,7 +354,8 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)  	wpf->entity.index = index;  	sprintf(name, "wpf.%u", index); -	ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops); +	ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops, +			       MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);  	if (ret < 0)  		return ERR_PTR(ret); @@ -228,13 +367,15 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)  	}  	/* Initialize the control handler. */ -	ret = vsp1_rwpf_init_ctrls(wpf); +	ret = wpf_init_controls(wpf);  	if (ret < 0) {  		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",  			index);  		goto error;  	} +	v4l2_ctrl_handler_setup(&wpf->ctrls); +  	return wpf;  error:  |