diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_rpf.c')
| -rw-r--r-- | drivers/media/platform/vsp1/vsp1_rpf.c | 226 | 
1 files changed, 226 insertions, 0 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c new file mode 100644 index 00000000000..c3d98642a4a --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -0,0 +1,226 @@ +/* + * vsp1_rpf.c  --  R-Car VSP1 Read Pixel Formatter + * + * Copyright (C) 2013-2014 Renesas Electronics 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 <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define RPF_MAX_WIDTH				8190 +#define RPF_MAX_HEIGHT				8190 + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg) +{ +	return vsp1_read(rpf->entity.vsp1, +			 reg + rpf->entity.index * VI6_RPF_OFFSET); +} + +static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) +{ +	vsp1_write(rpf->entity.vsp1, +		   reg + rpf->entity.index * VI6_RPF_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +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, 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, +		       (crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | +		       (crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); +	vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, +		       (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) { +		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); + +	/* Format */ +	infmt = VI6_RPF_INFMT_CIPM +	      | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); + +	if (fmtinfo->swap_yc) +		infmt |= VI6_RPF_INFMT_SPYCS; +	if (fmtinfo->swap_uv) +		infmt |= VI6_RPF_INFMT_SPUVS; + +	if (rpf->entity.formats[RWPF_PAD_SINK].code != +	    rpf->entity.formats[RWPF_PAD_SOURCE].code) +		infmt |= VI6_RPF_INFMT_CSC; + +	vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); +	vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); + +	/* 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. +	 */ +	vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED); +	vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, +		       255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); +	vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); +	vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); + +	return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops rpf_video_ops = { +	.s_stream = rpf_s_stream, +}; + +static struct v4l2_subdev_pad_ops rpf_pad_ops = { +	.enum_mbus_code = vsp1_rwpf_enum_mbus_code, +	.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 = { +	.video	= &rpf_video_ops, +	.pad    = &rpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Video Device Operations + */ + +static void rpf_vdev_queue(struct vsp1_video *video, +			   struct vsp1_video_buffer *buf) +{ +	struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); + +	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] + rpf->offsets[1]); +	if (buf->buf.num_planes > 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 = { +	.queue = rpf_vdev_queue, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) +{ +	struct v4l2_subdev *subdev; +	struct vsp1_video *video; +	struct vsp1_rwpf *rpf; +	int ret; + +	rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); +	if (rpf == NULL) +		return ERR_PTR(-ENOMEM); + +	rpf->max_width = RPF_MAX_WIDTH; +	rpf->max_height = RPF_MAX_HEIGHT; + +	rpf->entity.type = VSP1_ENTITY_RPF; +	rpf->entity.index = index; + +	ret = vsp1_entity_init(vsp1, &rpf->entity, 2); +	if (ret < 0) +		return ERR_PTR(ret); + +	/* Initialize the V4L2 subdev. */ +	subdev = &rpf->entity.subdev; +	v4l2_subdev_init(subdev, &rpf_ops); + +	subdev->entity.ops = &vsp1_media_ops; +	subdev->internal_ops = &vsp1_subdev_internal_ops; +	snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", +		 dev_name(vsp1->dev), index); +	v4l2_set_subdevdata(subdev, rpf); +	subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + +	vsp1_entity_init_formats(subdev, NULL); + +	/* Initialize the video device. */ +	video = &rpf->video; + +	video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; +	video->vsp1 = vsp1; +	video->ops = &rpf_vdev_ops; + +	ret = vsp1_video_init(video, &rpf->entity); +	if (ret < 0) +		goto error_video; + +	/* Connect the video device to the RPF. */ +	ret = media_entity_create_link(&rpf->video.video.entity, 0, +				       &rpf->entity.subdev.entity, +				       RWPF_PAD_SINK, +				       MEDIA_LNK_FL_ENABLED | +				       MEDIA_LNK_FL_IMMUTABLE); +	if (ret < 0) +		goto error_link; + +	return rpf; + +error_link: +	vsp1_video_cleanup(video); +error_video: +	media_entity_cleanup(&rpf->entity.subdev.entity); +	return ERR_PTR(ret); +}  | 
