18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vimc-scaler.c Virtual Media Controller Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
108c2ecf20Sopenharmony_ci#include <linux/v4l2-mediabus.h>
118c2ecf20Sopenharmony_ci#include <media/v4l2-rect.h>
128c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "vimc-common.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic unsigned int sca_mult = 3;
178c2ecf20Sopenharmony_cimodule_param(sca_mult, uint, 0000);
188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sca_mult, " the image size multiplier");
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define MAX_ZOOM	8
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define VIMC_SCA_FMT_WIDTH_DEFAULT  640
238c2ecf20Sopenharmony_ci#define VIMC_SCA_FMT_HEIGHT_DEFAULT 480
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct vimc_sca_device {
268c2ecf20Sopenharmony_ci	struct vimc_ent_device ved;
278c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
288c2ecf20Sopenharmony_ci	/* NOTE: the source fmt is the same as the sink
298c2ecf20Sopenharmony_ci	 * with the width and hight multiplied by mult
308c2ecf20Sopenharmony_ci	 */
318c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt sink_fmt;
328c2ecf20Sopenharmony_ci	struct v4l2_rect crop_rect;
338c2ecf20Sopenharmony_ci	/* Values calculated when the stream starts */
348c2ecf20Sopenharmony_ci	u8 *src_frame;
358c2ecf20Sopenharmony_ci	unsigned int src_line_size;
368c2ecf20Sopenharmony_ci	unsigned int bpp;
378c2ecf20Sopenharmony_ci	struct media_pad pads[2];
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic const struct v4l2_mbus_framefmt sink_fmt_default = {
418c2ecf20Sopenharmony_ci	.width = VIMC_SCA_FMT_WIDTH_DEFAULT,
428c2ecf20Sopenharmony_ci	.height = VIMC_SCA_FMT_HEIGHT_DEFAULT,
438c2ecf20Sopenharmony_ci	.code = MEDIA_BUS_FMT_RGB888_1X24,
448c2ecf20Sopenharmony_ci	.field = V4L2_FIELD_NONE,
458c2ecf20Sopenharmony_ci	.colorspace = V4L2_COLORSPACE_SRGB,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const struct v4l2_rect crop_rect_default = {
498c2ecf20Sopenharmony_ci	.width = VIMC_SCA_FMT_WIDTH_DEFAULT,
508c2ecf20Sopenharmony_ci	.height = VIMC_SCA_FMT_HEIGHT_DEFAULT,
518c2ecf20Sopenharmony_ci	.top = 0,
528c2ecf20Sopenharmony_ci	.left = 0,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const struct v4l2_rect crop_rect_min = {
568c2ecf20Sopenharmony_ci	.width = VIMC_FRAME_MIN_WIDTH,
578c2ecf20Sopenharmony_ci	.height = VIMC_FRAME_MIN_HEIGHT,
588c2ecf20Sopenharmony_ci	.top = 0,
598c2ecf20Sopenharmony_ci	.left = 0,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct v4l2_rect
638c2ecf20Sopenharmony_civimc_sca_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	/* Get the crop bounds to clamp the crop rectangle correctly */
668c2ecf20Sopenharmony_ci	struct v4l2_rect r = {
678c2ecf20Sopenharmony_ci		.left = 0,
688c2ecf20Sopenharmony_ci		.top = 0,
698c2ecf20Sopenharmony_ci		.width = sink_fmt->width,
708c2ecf20Sopenharmony_ci		.height = sink_fmt->height,
718c2ecf20Sopenharmony_ci	};
728c2ecf20Sopenharmony_ci	return r;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void vimc_sca_adjust_sink_crop(struct v4l2_rect *r,
768c2ecf20Sopenharmony_ci				      const struct v4l2_mbus_framefmt *sink_fmt)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	const struct v4l2_rect sink_rect =
798c2ecf20Sopenharmony_ci		vimc_sca_get_crop_bound_sink(sink_fmt);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/* Disallow rectangles smaller than the minimal one. */
828c2ecf20Sopenharmony_ci	v4l2_rect_set_min_size(r, &crop_rect_min);
838c2ecf20Sopenharmony_ci	v4l2_rect_map_inside(r, &sink_rect);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int vimc_sca_init_cfg(struct v4l2_subdev *sd,
878c2ecf20Sopenharmony_ci			     struct v4l2_subdev_pad_config *cfg)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *mf;
908c2ecf20Sopenharmony_ci	struct v4l2_rect *r;
918c2ecf20Sopenharmony_ci	unsigned int i;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mf = v4l2_subdev_get_try_format(sd, cfg, 0);
948c2ecf20Sopenharmony_ci	*mf = sink_fmt_default;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	r = v4l2_subdev_get_try_crop(sd, cfg, 0);
978c2ecf20Sopenharmony_ci	*r = crop_rect_default;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	for (i = 1; i < sd->entity.num_pads; i++) {
1008c2ecf20Sopenharmony_ci		mf = v4l2_subdev_get_try_format(sd, cfg, i);
1018c2ecf20Sopenharmony_ci		*mf = sink_fmt_default;
1028c2ecf20Sopenharmony_ci		mf->width = mf->width * sca_mult;
1038c2ecf20Sopenharmony_ci		mf->height = mf->height * sca_mult;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int vimc_sca_enum_mbus_code(struct v4l2_subdev *sd,
1108c2ecf20Sopenharmony_ci				   struct v4l2_subdev_pad_config *cfg,
1118c2ecf20Sopenharmony_ci				   struct v4l2_subdev_mbus_code_enum *code)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	u32 mbus_code = vimc_mbus_code_by_index(code->index);
1148c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (!mbus_code)
1178c2ecf20Sopenharmony_ci		return -EINVAL;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_code(mbus_code);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* We don't support bayer format */
1228c2ecf20Sopenharmony_ci	if (!vpix || vpix->bayer)
1238c2ecf20Sopenharmony_ci		return -EINVAL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	code->code = mbus_code;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int vimc_sca_enum_frame_size(struct v4l2_subdev *sd,
1318c2ecf20Sopenharmony_ci				    struct v4l2_subdev_pad_config *cfg,
1328c2ecf20Sopenharmony_ci				    struct v4l2_subdev_frame_size_enum *fse)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (fse->index)
1378c2ecf20Sopenharmony_ci		return -EINVAL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Only accept code in the pix map table in non bayer format */
1408c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_code(fse->code);
1418c2ecf20Sopenharmony_ci	if (!vpix || vpix->bayer)
1428c2ecf20Sopenharmony_ci		return -EINVAL;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	fse->min_width = VIMC_FRAME_MIN_WIDTH;
1458c2ecf20Sopenharmony_ci	fse->min_height = VIMC_FRAME_MIN_HEIGHT;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (VIMC_IS_SINK(fse->pad)) {
1488c2ecf20Sopenharmony_ci		fse->max_width = VIMC_FRAME_MAX_WIDTH;
1498c2ecf20Sopenharmony_ci		fse->max_height = VIMC_FRAME_MAX_HEIGHT;
1508c2ecf20Sopenharmony_ci	} else {
1518c2ecf20Sopenharmony_ci		fse->max_width = VIMC_FRAME_MAX_WIDTH * MAX_ZOOM;
1528c2ecf20Sopenharmony_ci		fse->max_height = VIMC_FRAME_MAX_HEIGHT * MAX_ZOOM;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return 0;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int vimc_sca_get_fmt(struct v4l2_subdev *sd,
1598c2ecf20Sopenharmony_ci			    struct v4l2_subdev_pad_config *cfg,
1608c2ecf20Sopenharmony_ci			    struct v4l2_subdev_format *format)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
1638c2ecf20Sopenharmony_ci	struct v4l2_rect *crop_rect;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Get the current sink format */
1668c2ecf20Sopenharmony_ci	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1678c2ecf20Sopenharmony_ci		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
1688c2ecf20Sopenharmony_ci		crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0);
1698c2ecf20Sopenharmony_ci	} else {
1708c2ecf20Sopenharmony_ci		format->format = vsca->sink_fmt;
1718c2ecf20Sopenharmony_ci		crop_rect = &vsca->crop_rect;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Scale the frame size for the source pad */
1758c2ecf20Sopenharmony_ci	if (VIMC_IS_SRC(format->pad)) {
1768c2ecf20Sopenharmony_ci		format->format.width = crop_rect->width * sca_mult;
1778c2ecf20Sopenharmony_ci		format->format.height = crop_rect->height * sca_mult;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void vimc_sca_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	const struct vimc_pix_map *vpix;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* Only accept code in the pix map table in non bayer format */
1888c2ecf20Sopenharmony_ci	vpix = vimc_pix_map_by_code(fmt->code);
1898c2ecf20Sopenharmony_ci	if (!vpix || vpix->bayer)
1908c2ecf20Sopenharmony_ci		fmt->code = sink_fmt_default.code;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH,
1938c2ecf20Sopenharmony_ci			     VIMC_FRAME_MAX_WIDTH) & ~1;
1948c2ecf20Sopenharmony_ci	fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT,
1958c2ecf20Sopenharmony_ci			      VIMC_FRAME_MAX_HEIGHT) & ~1;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (fmt->field == V4L2_FIELD_ANY)
1988c2ecf20Sopenharmony_ci		fmt->field = sink_fmt_default.field;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	vimc_colorimetry_clamp(fmt);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int vimc_sca_set_fmt(struct v4l2_subdev *sd,
2048c2ecf20Sopenharmony_ci			    struct v4l2_subdev_pad_config *cfg,
2058c2ecf20Sopenharmony_ci			    struct v4l2_subdev_format *fmt)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
2088c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *sink_fmt;
2098c2ecf20Sopenharmony_ci	struct v4l2_rect *crop_rect;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
2128c2ecf20Sopenharmony_ci		/* Do not change the format while stream is on */
2138c2ecf20Sopenharmony_ci		if (vsca->src_frame)
2148c2ecf20Sopenharmony_ci			return -EBUSY;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		sink_fmt = &vsca->sink_fmt;
2178c2ecf20Sopenharmony_ci		crop_rect = &vsca->crop_rect;
2188c2ecf20Sopenharmony_ci	} else {
2198c2ecf20Sopenharmony_ci		sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
2208c2ecf20Sopenharmony_ci		crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0);
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/*
2248c2ecf20Sopenharmony_ci	 * Do not change the format of the source pad,
2258c2ecf20Sopenharmony_ci	 * it is propagated from the sink
2268c2ecf20Sopenharmony_ci	 */
2278c2ecf20Sopenharmony_ci	if (VIMC_IS_SRC(fmt->pad)) {
2288c2ecf20Sopenharmony_ci		fmt->format = *sink_fmt;
2298c2ecf20Sopenharmony_ci		fmt->format.width = crop_rect->width * sca_mult;
2308c2ecf20Sopenharmony_ci		fmt->format.height = crop_rect->height * sca_mult;
2318c2ecf20Sopenharmony_ci	} else {
2328c2ecf20Sopenharmony_ci		/* Set the new format in the sink pad */
2338c2ecf20Sopenharmony_ci		vimc_sca_adjust_sink_fmt(&fmt->format);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		dev_dbg(vsca->ved.dev, "%s: sink format update: "
2368c2ecf20Sopenharmony_ci			"old:%dx%d (0x%x, %d, %d, %d, %d) "
2378c2ecf20Sopenharmony_ci			"new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsca->sd.name,
2388c2ecf20Sopenharmony_ci			/* old */
2398c2ecf20Sopenharmony_ci			sink_fmt->width, sink_fmt->height, sink_fmt->code,
2408c2ecf20Sopenharmony_ci			sink_fmt->colorspace, sink_fmt->quantization,
2418c2ecf20Sopenharmony_ci			sink_fmt->xfer_func, sink_fmt->ycbcr_enc,
2428c2ecf20Sopenharmony_ci			/* new */
2438c2ecf20Sopenharmony_ci			fmt->format.width, fmt->format.height, fmt->format.code,
2448c2ecf20Sopenharmony_ci			fmt->format.colorspace,	fmt->format.quantization,
2458c2ecf20Sopenharmony_ci			fmt->format.xfer_func, fmt->format.ycbcr_enc);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		*sink_fmt = fmt->format;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		/* Do the crop, but respect the current bounds */
2508c2ecf20Sopenharmony_ci		vimc_sca_adjust_sink_crop(crop_rect, sink_fmt);
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int vimc_sca_get_selection(struct v4l2_subdev *sd,
2578c2ecf20Sopenharmony_ci				  struct v4l2_subdev_pad_config *cfg,
2588c2ecf20Sopenharmony_ci				  struct v4l2_subdev_selection *sel)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
2618c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *sink_fmt;
2628c2ecf20Sopenharmony_ci	struct v4l2_rect *crop_rect;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (VIMC_IS_SRC(sel->pad))
2658c2ecf20Sopenharmony_ci		return -EINVAL;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
2688c2ecf20Sopenharmony_ci		sink_fmt = &vsca->sink_fmt;
2698c2ecf20Sopenharmony_ci		crop_rect = &vsca->crop_rect;
2708c2ecf20Sopenharmony_ci	} else {
2718c2ecf20Sopenharmony_ci		sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
2728c2ecf20Sopenharmony_ci		crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	switch (sel->target) {
2768c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
2778c2ecf20Sopenharmony_ci		sel->r = *crop_rect;
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
2808c2ecf20Sopenharmony_ci		sel->r = vimc_sca_get_crop_bound_sink(sink_fmt);
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	default:
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int vimc_sca_set_selection(struct v4l2_subdev *sd,
2908c2ecf20Sopenharmony_ci				  struct v4l2_subdev_pad_config *cfg,
2918c2ecf20Sopenharmony_ci				  struct v4l2_subdev_selection *sel)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
2948c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *sink_fmt;
2958c2ecf20Sopenharmony_ci	struct v4l2_rect *crop_rect;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (VIMC_IS_SRC(sel->pad))
2988c2ecf20Sopenharmony_ci		return -EINVAL;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
3018c2ecf20Sopenharmony_ci		/* Do not change the format while stream is on */
3028c2ecf20Sopenharmony_ci		if (vsca->src_frame)
3038c2ecf20Sopenharmony_ci			return -EBUSY;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		crop_rect = &vsca->crop_rect;
3068c2ecf20Sopenharmony_ci		sink_fmt = &vsca->sink_fmt;
3078c2ecf20Sopenharmony_ci	} else {
3088c2ecf20Sopenharmony_ci		crop_rect = v4l2_subdev_get_try_crop(sd, cfg, 0);
3098c2ecf20Sopenharmony_ci		sink_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	switch (sel->target) {
3138c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
3148c2ecf20Sopenharmony_ci		/* Do the crop, but respect the current bounds */
3158c2ecf20Sopenharmony_ci		vimc_sca_adjust_sink_crop(&sel->r, sink_fmt);
3168c2ecf20Sopenharmony_ci		*crop_rect = sel->r;
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	default:
3198c2ecf20Sopenharmony_ci		return -EINVAL;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vimc_sca_pad_ops = {
3268c2ecf20Sopenharmony_ci	.init_cfg		= vimc_sca_init_cfg,
3278c2ecf20Sopenharmony_ci	.enum_mbus_code		= vimc_sca_enum_mbus_code,
3288c2ecf20Sopenharmony_ci	.enum_frame_size	= vimc_sca_enum_frame_size,
3298c2ecf20Sopenharmony_ci	.get_fmt		= vimc_sca_get_fmt,
3308c2ecf20Sopenharmony_ci	.set_fmt		= vimc_sca_set_fmt,
3318c2ecf20Sopenharmony_ci	.get_selection		= vimc_sca_get_selection,
3328c2ecf20Sopenharmony_ci	.set_selection		= vimc_sca_set_selection,
3338c2ecf20Sopenharmony_ci};
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = v4l2_get_subdevdata(sd);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (enable) {
3408c2ecf20Sopenharmony_ci		const struct vimc_pix_map *vpix;
3418c2ecf20Sopenharmony_ci		unsigned int frame_size;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		if (vsca->src_frame)
3448c2ecf20Sopenharmony_ci			return 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		/* Save the bytes per pixel of the sink */
3478c2ecf20Sopenharmony_ci		vpix = vimc_pix_map_by_code(vsca->sink_fmt.code);
3488c2ecf20Sopenharmony_ci		vsca->bpp = vpix->bpp;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		/* Calculate the width in bytes of the src frame */
3518c2ecf20Sopenharmony_ci		vsca->src_line_size = vsca->crop_rect.width *
3528c2ecf20Sopenharmony_ci				      sca_mult * vsca->bpp;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		/* Calculate the frame size of the source pad */
3558c2ecf20Sopenharmony_ci		frame_size = vsca->src_line_size * vsca->crop_rect.height *
3568c2ecf20Sopenharmony_ci			     sca_mult;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		/* Allocate the frame buffer. Use vmalloc to be able to
3598c2ecf20Sopenharmony_ci		 * allocate a large amount of memory
3608c2ecf20Sopenharmony_ci		 */
3618c2ecf20Sopenharmony_ci		vsca->src_frame = vmalloc(frame_size);
3628c2ecf20Sopenharmony_ci		if (!vsca->src_frame)
3638c2ecf20Sopenharmony_ci			return -ENOMEM;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	} else {
3668c2ecf20Sopenharmony_ci		if (!vsca->src_frame)
3678c2ecf20Sopenharmony_ci			return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		vfree(vsca->src_frame);
3708c2ecf20Sopenharmony_ci		vsca->src_frame = NULL;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops vimc_sca_video_ops = {
3778c2ecf20Sopenharmony_ci	.s_stream = vimc_sca_s_stream,
3788c2ecf20Sopenharmony_ci};
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops vimc_sca_ops = {
3818c2ecf20Sopenharmony_ci	.pad = &vimc_sca_pad_ops,
3828c2ecf20Sopenharmony_ci	.video = &vimc_sca_video_ops,
3838c2ecf20Sopenharmony_ci};
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void vimc_sca_fill_pix(u8 *const ptr,
3868c2ecf20Sopenharmony_ci			      const u8 *const pixel,
3878c2ecf20Sopenharmony_ci			      const unsigned int bpp)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	unsigned int i;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* copy the pixel to the pointer */
3928c2ecf20Sopenharmony_ci	for (i = 0; i < bpp; i++)
3938c2ecf20Sopenharmony_ci		ptr[i] = pixel[i];
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic void vimc_sca_scale_pix(const struct vimc_sca_device *const vsca,
3978c2ecf20Sopenharmony_ci			       unsigned int lin, unsigned int col,
3988c2ecf20Sopenharmony_ci			       const u8 *const sink_frame)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	const struct v4l2_rect crop_rect = vsca->crop_rect;
4018c2ecf20Sopenharmony_ci	unsigned int i, j, index;
4028c2ecf20Sopenharmony_ci	const u8 *pixel;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Point to the pixel value in position (lin, col) in the sink frame */
4058c2ecf20Sopenharmony_ci	index = VIMC_FRAME_INDEX(lin, col,
4068c2ecf20Sopenharmony_ci				 vsca->sink_fmt.width,
4078c2ecf20Sopenharmony_ci				 vsca->bpp);
4088c2ecf20Sopenharmony_ci	pixel = &sink_frame[index];
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	dev_dbg(vsca->ved.dev,
4118c2ecf20Sopenharmony_ci		"sca: %s: --- scale_pix sink pos %dx%d, index %d ---\n",
4128c2ecf20Sopenharmony_ci		vsca->sd.name, lin, col, index);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* point to the place we are going to put the first pixel
4158c2ecf20Sopenharmony_ci	 * in the scaled src frame
4168c2ecf20Sopenharmony_ci	 */
4178c2ecf20Sopenharmony_ci	lin -= crop_rect.top;
4188c2ecf20Sopenharmony_ci	col -= crop_rect.left;
4198c2ecf20Sopenharmony_ci	index = VIMC_FRAME_INDEX(lin * sca_mult, col * sca_mult,
4208c2ecf20Sopenharmony_ci				 crop_rect.width * sca_mult, vsca->bpp);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	dev_dbg(vsca->ved.dev, "sca: %s: scale_pix src pos %dx%d, index %d\n",
4238c2ecf20Sopenharmony_ci		vsca->sd.name, lin * sca_mult, col * sca_mult, index);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Repeat this pixel mult times */
4268c2ecf20Sopenharmony_ci	for (i = 0; i < sca_mult; i++) {
4278c2ecf20Sopenharmony_ci		/* Iterate through each beginning of a
4288c2ecf20Sopenharmony_ci		 * pixel repetition in a line
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci		for (j = 0; j < sca_mult * vsca->bpp; j += vsca->bpp) {
4318c2ecf20Sopenharmony_ci			dev_dbg(vsca->ved.dev,
4328c2ecf20Sopenharmony_ci				"sca: %s: sca: scale_pix src pos %d\n",
4338c2ecf20Sopenharmony_ci				vsca->sd.name, index + j);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci			/* copy the pixel to the position index + j */
4368c2ecf20Sopenharmony_ci			vimc_sca_fill_pix(&vsca->src_frame[index + j],
4378c2ecf20Sopenharmony_ci					  pixel, vsca->bpp);
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		/* move the index to the next line */
4418c2ecf20Sopenharmony_ci		index += vsca->src_line_size;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void vimc_sca_fill_src_frame(const struct vimc_sca_device *const vsca,
4468c2ecf20Sopenharmony_ci				    const u8 *const sink_frame)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	const struct v4l2_rect r = vsca->crop_rect;
4498c2ecf20Sopenharmony_ci	unsigned int i, j;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* Scale each pixel from the original sink frame */
4528c2ecf20Sopenharmony_ci	/* TODO: implement scale down, only scale up is supported for now */
4538c2ecf20Sopenharmony_ci	for (i = r.top; i < r.top + r.height; i++)
4548c2ecf20Sopenharmony_ci		for (j = r.left; j < r.left + r.width; j++)
4558c2ecf20Sopenharmony_ci			vimc_sca_scale_pix(vsca, i, j, sink_frame);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic void *vimc_sca_process_frame(struct vimc_ent_device *ved,
4598c2ecf20Sopenharmony_ci				    const void *sink_frame)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca = container_of(ved, struct vimc_sca_device,
4628c2ecf20Sopenharmony_ci						    ved);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* If the stream in this node is not active, just return */
4658c2ecf20Sopenharmony_ci	if (!vsca->src_frame)
4668c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	vimc_sca_fill_src_frame(vsca, sink_frame);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return vsca->src_frame;
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic void vimc_sca_release(struct vimc_ent_device *ved)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca =
4768c2ecf20Sopenharmony_ci		container_of(ved, struct vimc_sca_device, ved);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	media_entity_cleanup(vsca->ved.ent);
4798c2ecf20Sopenharmony_ci	kfree(vsca);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic struct vimc_ent_device *vimc_sca_add(struct vimc_device *vimc,
4838c2ecf20Sopenharmony_ci					    const char *vcfg_name)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
4868c2ecf20Sopenharmony_ci	struct vimc_sca_device *vsca;
4878c2ecf20Sopenharmony_ci	int ret;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* Allocate the vsca struct */
4908c2ecf20Sopenharmony_ci	vsca = kzalloc(sizeof(*vsca), GFP_KERNEL);
4918c2ecf20Sopenharmony_ci	if (!vsca)
4928c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Initialize ved and sd */
4958c2ecf20Sopenharmony_ci	vsca->pads[0].flags = MEDIA_PAD_FL_SINK;
4968c2ecf20Sopenharmony_ci	vsca->pads[1].flags = MEDIA_PAD_FL_SOURCE;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	ret = vimc_ent_sd_register(&vsca->ved, &vsca->sd, v4l2_dev,
4998c2ecf20Sopenharmony_ci				   vcfg_name,
5008c2ecf20Sopenharmony_ci				   MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
5018c2ecf20Sopenharmony_ci				   vsca->pads, &vimc_sca_ops);
5028c2ecf20Sopenharmony_ci	if (ret) {
5038c2ecf20Sopenharmony_ci		kfree(vsca);
5048c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	vsca->ved.process_frame = vimc_sca_process_frame;
5088c2ecf20Sopenharmony_ci	vsca->ved.dev = vimc->mdev.dev;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* Initialize the frame format */
5118c2ecf20Sopenharmony_ci	vsca->sink_fmt = sink_fmt_default;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* Initialize the crop selection */
5148c2ecf20Sopenharmony_ci	vsca->crop_rect = crop_rect_default;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return &vsca->ved;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistruct vimc_ent_type vimc_sca_type = {
5208c2ecf20Sopenharmony_ci	.add = vimc_sca_add,
5218c2ecf20Sopenharmony_ci	.release = vimc_sca_release
5228c2ecf20Sopenharmony_ci};
523