18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vsp1_rwpf.c  --  R-Car VSP1 Read and Write Pixel Formatters
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Renesas Electronics Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "vsp1.h"
138c2ecf20Sopenharmony_ci#include "vsp1_rwpf.h"
148c2ecf20Sopenharmony_ci#include "vsp1_video.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define RWPF_MIN_WIDTH				1
178c2ecf20Sopenharmony_ci#define RWPF_MIN_HEIGHT				1
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
208c2ecf20Sopenharmony_ci				     struct v4l2_subdev_pad_config *config)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config,
238c2ecf20Sopenharmony_ci					RWPF_PAD_SINK);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
278c2ecf20Sopenharmony_ci * V4L2 Subdevice Pad Operations
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
318c2ecf20Sopenharmony_ci				    struct v4l2_subdev_pad_config *cfg,
328c2ecf20Sopenharmony_ci				    struct v4l2_subdev_mbus_code_enum *code)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	static const unsigned int codes[] = {
358c2ecf20Sopenharmony_ci		MEDIA_BUS_FMT_ARGB8888_1X32,
368c2ecf20Sopenharmony_ci		MEDIA_BUS_FMT_AHSV8888_1X32,
378c2ecf20Sopenharmony_ci		MEDIA_BUS_FMT_AYUV8_1X32,
388c2ecf20Sopenharmony_ci	};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (code->index >= ARRAY_SIZE(codes))
418c2ecf20Sopenharmony_ci		return -EINVAL;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	code->code = codes[code->index];
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
498c2ecf20Sopenharmony_ci				     struct v4l2_subdev_pad_config *cfg,
508c2ecf20Sopenharmony_ci				     struct v4l2_subdev_frame_size_enum *fse)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH,
558c2ecf20Sopenharmony_ci					   RWPF_MIN_HEIGHT, rwpf->max_width,
568c2ecf20Sopenharmony_ci					   rwpf->max_height);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
608c2ecf20Sopenharmony_ci				struct v4l2_subdev_pad_config *cfg,
618c2ecf20Sopenharmony_ci				struct v4l2_subdev_format *fmt)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
648c2ecf20Sopenharmony_ci	struct v4l2_subdev_pad_config *config;
658c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
668c2ecf20Sopenharmony_ci	int ret = 0;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	mutex_lock(&rwpf->entity.lock);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which);
718c2ecf20Sopenharmony_ci	if (!config) {
728c2ecf20Sopenharmony_ci		ret = -EINVAL;
738c2ecf20Sopenharmony_ci		goto done;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Default to YUV if the requested format is not supported. */
778c2ecf20Sopenharmony_ci	if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
788c2ecf20Sopenharmony_ci	    fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
798c2ecf20Sopenharmony_ci	    fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
808c2ecf20Sopenharmony_ci		fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (fmt->pad == RWPF_PAD_SOURCE) {
858c2ecf20Sopenharmony_ci		/*
868c2ecf20Sopenharmony_ci		 * The RWPF performs format conversion but can't scale, only the
878c2ecf20Sopenharmony_ci		 * format code can be changed on the source pad.
888c2ecf20Sopenharmony_ci		 */
898c2ecf20Sopenharmony_ci		format->code = fmt->format.code;
908c2ecf20Sopenharmony_ci		fmt->format = *format;
918c2ecf20Sopenharmony_ci		goto done;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	format->code = fmt->format.code;
958c2ecf20Sopenharmony_ci	format->width = clamp_t(unsigned int, fmt->format.width,
968c2ecf20Sopenharmony_ci				RWPF_MIN_WIDTH, rwpf->max_width);
978c2ecf20Sopenharmony_ci	format->height = clamp_t(unsigned int, fmt->format.height,
988c2ecf20Sopenharmony_ci				 RWPF_MIN_HEIGHT, rwpf->max_height);
998c2ecf20Sopenharmony_ci	format->field = V4L2_FIELD_NONE;
1008c2ecf20Sopenharmony_ci	format->colorspace = V4L2_COLORSPACE_SRGB;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	fmt->format = *format;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (rwpf->entity.type == VSP1_ENTITY_RPF) {
1058c2ecf20Sopenharmony_ci		struct v4l2_rect *crop;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		/* Update the sink crop rectangle. */
1088c2ecf20Sopenharmony_ci		crop = vsp1_rwpf_get_crop(rwpf, config);
1098c2ecf20Sopenharmony_ci		crop->left = 0;
1108c2ecf20Sopenharmony_ci		crop->top = 0;
1118c2ecf20Sopenharmony_ci		crop->width = fmt->format.width;
1128c2ecf20Sopenharmony_ci		crop->height = fmt->format.height;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Propagate the format to the source pad. */
1168c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
1178c2ecf20Sopenharmony_ci					    RWPF_PAD_SOURCE);
1188c2ecf20Sopenharmony_ci	*format = fmt->format;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (rwpf->flip.rotate) {
1218c2ecf20Sopenharmony_ci		format->width = fmt->format.height;
1228c2ecf20Sopenharmony_ci		format->height = fmt->format.width;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cidone:
1268c2ecf20Sopenharmony_ci	mutex_unlock(&rwpf->entity.lock);
1278c2ecf20Sopenharmony_ci	return ret;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
1318c2ecf20Sopenharmony_ci				   struct v4l2_subdev_pad_config *cfg,
1328c2ecf20Sopenharmony_ci				   struct v4l2_subdev_selection *sel)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
1358c2ecf20Sopenharmony_ci	struct v4l2_subdev_pad_config *config;
1368c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
1378c2ecf20Sopenharmony_ci	int ret = 0;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	 * Cropping is only supported on the RPF and is implemented on the sink
1418c2ecf20Sopenharmony_ci	 * pad.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	mutex_lock(&rwpf->entity.lock);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
1498c2ecf20Sopenharmony_ci	if (!config) {
1508c2ecf20Sopenharmony_ci		ret = -EINVAL;
1518c2ecf20Sopenharmony_ci		goto done;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	switch (sel->target) {
1558c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
1568c2ecf20Sopenharmony_ci		sel->r = *vsp1_rwpf_get_crop(rwpf, config);
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
1608c2ecf20Sopenharmony_ci		format = vsp1_entity_get_pad_format(&rwpf->entity, config,
1618c2ecf20Sopenharmony_ci						    RWPF_PAD_SINK);
1628c2ecf20Sopenharmony_ci		sel->r.left = 0;
1638c2ecf20Sopenharmony_ci		sel->r.top = 0;
1648c2ecf20Sopenharmony_ci		sel->r.width = format->width;
1658c2ecf20Sopenharmony_ci		sel->r.height = format->height;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	default:
1698c2ecf20Sopenharmony_ci		ret = -EINVAL;
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cidone:
1748c2ecf20Sopenharmony_ci	mutex_unlock(&rwpf->entity.lock);
1758c2ecf20Sopenharmony_ci	return ret;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
1798c2ecf20Sopenharmony_ci				   struct v4l2_subdev_pad_config *cfg,
1808c2ecf20Sopenharmony_ci				   struct v4l2_subdev_selection *sel)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
1838c2ecf20Sopenharmony_ci	struct v4l2_subdev_pad_config *config;
1848c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
1858c2ecf20Sopenharmony_ci	struct v4l2_rect *crop;
1868c2ecf20Sopenharmony_ci	int ret = 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/*
1898c2ecf20Sopenharmony_ci	 * Cropping is only supported on the RPF and is implemented on the sink
1908c2ecf20Sopenharmony_ci	 * pad.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (sel->target != V4L2_SEL_TGT_CROP)
1968c2ecf20Sopenharmony_ci		return -EINVAL;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	mutex_lock(&rwpf->entity.lock);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which);
2018c2ecf20Sopenharmony_ci	if (!config) {
2028c2ecf20Sopenharmony_ci		ret = -EINVAL;
2038c2ecf20Sopenharmony_ci		goto done;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Make sure the crop rectangle is entirely contained in the image. */
2078c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
2088c2ecf20Sopenharmony_ci					    RWPF_PAD_SINK);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/*
2118c2ecf20Sopenharmony_ci	 * Restrict the crop rectangle coordinates to multiples of 2 to avoid
2128c2ecf20Sopenharmony_ci	 * shifting the color plane.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
2158c2ecf20Sopenharmony_ci		sel->r.left = ALIGN(sel->r.left, 2);
2168c2ecf20Sopenharmony_ci		sel->r.top = ALIGN(sel->r.top, 2);
2178c2ecf20Sopenharmony_ci		sel->r.width = round_down(sel->r.width, 2);
2188c2ecf20Sopenharmony_ci		sel->r.height = round_down(sel->r.height, 2);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
2228c2ecf20Sopenharmony_ci	sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
2238c2ecf20Sopenharmony_ci	sel->r.width = min_t(unsigned int, sel->r.width,
2248c2ecf20Sopenharmony_ci			     format->width - sel->r.left);
2258c2ecf20Sopenharmony_ci	sel->r.height = min_t(unsigned int, sel->r.height,
2268c2ecf20Sopenharmony_ci			      format->height - sel->r.top);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	crop = vsp1_rwpf_get_crop(rwpf, config);
2298c2ecf20Sopenharmony_ci	*crop = sel->r;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* Propagate the format to the source pad. */
2328c2ecf20Sopenharmony_ci	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
2338c2ecf20Sopenharmony_ci					    RWPF_PAD_SOURCE);
2348c2ecf20Sopenharmony_ci	format->width = crop->width;
2358c2ecf20Sopenharmony_ci	format->height = crop->height;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cidone:
2388c2ecf20Sopenharmony_ci	mutex_unlock(&rwpf->entity.lock);
2398c2ecf20Sopenharmony_ci	return ret;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciconst struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
2438c2ecf20Sopenharmony_ci	.init_cfg = vsp1_entity_init_cfg,
2448c2ecf20Sopenharmony_ci	.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
2458c2ecf20Sopenharmony_ci	.enum_frame_size = vsp1_rwpf_enum_frame_size,
2468c2ecf20Sopenharmony_ci	.get_fmt = vsp1_subdev_get_pad_format,
2478c2ecf20Sopenharmony_ci	.set_fmt = vsp1_rwpf_set_format,
2488c2ecf20Sopenharmony_ci	.get_selection = vsp1_rwpf_get_selection,
2498c2ecf20Sopenharmony_ci	.set_selection = vsp1_rwpf_set_selection,
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2538c2ecf20Sopenharmony_ci * Controls
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct vsp1_rwpf *rwpf =
2598c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	switch (ctrl->id) {
2628c2ecf20Sopenharmony_ci	case V4L2_CID_ALPHA_COMPONENT:
2638c2ecf20Sopenharmony_ci		rwpf->alpha = ctrl->val;
2648c2ecf20Sopenharmony_ci		break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
2718c2ecf20Sopenharmony_ci	.s_ctrl = vsp1_rwpf_s_ctrl,
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ciint vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
2778c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
2788c2ecf20Sopenharmony_ci			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return rwpf->ctrls.error;
2838c2ecf20Sopenharmony_ci}
284