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