162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vimc-scaler.c Virtual Media Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/moduleparam.h> 962306a36Sopenharmony_ci#include <linux/string.h> 1062306a36Sopenharmony_ci#include <linux/vmalloc.h> 1162306a36Sopenharmony_ci#include <linux/v4l2-mediabus.h> 1262306a36Sopenharmony_ci#include <media/v4l2-rect.h> 1362306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "vimc-common.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Pad identifier */ 1862306a36Sopenharmony_cienum vic_sca_pad { 1962306a36Sopenharmony_ci VIMC_SCALER_SINK = 0, 2062306a36Sopenharmony_ci VIMC_SCALER_SRC = 1, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define VIMC_SCALER_FMT_WIDTH_DEFAULT 640 2462306a36Sopenharmony_ci#define VIMC_SCALER_FMT_HEIGHT_DEFAULT 480 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct vimc_scaler_device { 2762306a36Sopenharmony_ci struct vimc_ent_device ved; 2862306a36Sopenharmony_ci struct v4l2_subdev sd; 2962306a36Sopenharmony_ci struct v4l2_rect crop_rect; 3062306a36Sopenharmony_ci /* Frame format for both sink and src pad */ 3162306a36Sopenharmony_ci struct v4l2_mbus_framefmt fmt[2]; 3262306a36Sopenharmony_ci /* Values calculated when the stream starts */ 3362306a36Sopenharmony_ci u8 *src_frame; 3462306a36Sopenharmony_ci unsigned int bpp; 3562306a36Sopenharmony_ci struct media_pad pads[2]; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic const struct v4l2_mbus_framefmt fmt_default = { 3962306a36Sopenharmony_ci .width = VIMC_SCALER_FMT_WIDTH_DEFAULT, 4062306a36Sopenharmony_ci .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT, 4162306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB888_1X24, 4262306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 4362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const struct v4l2_rect crop_rect_default = { 4762306a36Sopenharmony_ci .width = VIMC_SCALER_FMT_WIDTH_DEFAULT, 4862306a36Sopenharmony_ci .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT, 4962306a36Sopenharmony_ci .top = 0, 5062306a36Sopenharmony_ci .left = 0, 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const struct v4l2_rect crop_rect_min = { 5462306a36Sopenharmony_ci .width = VIMC_FRAME_MIN_WIDTH, 5562306a36Sopenharmony_ci .height = VIMC_FRAME_MIN_HEIGHT, 5662306a36Sopenharmony_ci .top = 0, 5762306a36Sopenharmony_ci .left = 0, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic struct v4l2_rect 6162306a36Sopenharmony_civimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci /* Get the crop bounds to clamp the crop rectangle correctly */ 6462306a36Sopenharmony_ci struct v4l2_rect r = { 6562306a36Sopenharmony_ci .left = 0, 6662306a36Sopenharmony_ci .top = 0, 6762306a36Sopenharmony_ci .width = sink_fmt->width, 6862306a36Sopenharmony_ci .height = sink_fmt->height, 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci return r; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int vimc_scaler_init_cfg(struct v4l2_subdev *sd, 7462306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 7762306a36Sopenharmony_ci struct v4l2_rect *r; 7862306a36Sopenharmony_ci unsigned int i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci for (i = 0; i < sd->entity.num_pads; i++) { 8162306a36Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, sd_state, i); 8262306a36Sopenharmony_ci *mf = fmt_default; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci r = v4l2_subdev_get_try_crop(sd, sd_state, VIMC_SCALER_SINK); 8662306a36Sopenharmony_ci *r = crop_rect_default; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int vimc_scaler_enum_mbus_code(struct v4l2_subdev *sd, 9262306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 9362306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u32 mbus_code = vimc_mbus_code_by_index(code->index); 9662306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!mbus_code) 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(mbus_code); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* We don't support bayer format */ 10462306a36Sopenharmony_ci if (!vpix || vpix->bayer) 10562306a36Sopenharmony_ci return -EINVAL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci code->code = mbus_code; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd, 11362306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 11462306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (fse->index) 11962306a36Sopenharmony_ci return -EINVAL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Only accept code in the pix map table in non bayer format */ 12262306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(fse->code); 12362306a36Sopenharmony_ci if (!vpix || vpix->bayer) 12462306a36Sopenharmony_ci return -EINVAL; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci fse->min_width = VIMC_FRAME_MIN_WIDTH; 12762306a36Sopenharmony_ci fse->min_height = VIMC_FRAME_MIN_HEIGHT; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci fse->max_width = VIMC_FRAME_MAX_WIDTH; 13062306a36Sopenharmony_ci fse->max_height = VIMC_FRAME_MAX_HEIGHT; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt * 13662306a36Sopenharmony_civimc_scaler_pad_format(struct vimc_scaler_device *vscaler, 13762306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, u32 pad, 13862306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 14162306a36Sopenharmony_ci return v4l2_subdev_get_try_format(&vscaler->sd, sd_state, pad); 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci return &vscaler->fmt[pad]; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct v4l2_rect * 14762306a36Sopenharmony_civimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, 14862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 14962306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 15262306a36Sopenharmony_ci return v4l2_subdev_get_try_crop(&vscaler->sd, sd_state, 15362306a36Sopenharmony_ci VIMC_SCALER_SINK); 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci return &vscaler->crop_rect; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int vimc_scaler_get_fmt(struct v4l2_subdev *sd, 15962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 16062306a36Sopenharmony_ci struct v4l2_subdev_format *format) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad, 16562306a36Sopenharmony_ci format->which); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int vimc_scaler_set_fmt(struct v4l2_subdev *sd, 17062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 17162306a36Sopenharmony_ci struct v4l2_subdev_format *format) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); 17462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Do not change the active format while stream is on */ 17762306a36Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) 17862306a36Sopenharmony_ci return -EBUSY; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * The media bus code and colorspace can only be changed on the sink 18462306a36Sopenharmony_ci * pad, the source pad only follows. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (format->pad == VIMC_SCALER_SINK) { 18762306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Only accept code in the pix map table in non bayer format. */ 19062306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(format->format.code); 19162306a36Sopenharmony_ci if (vpix && !vpix->bayer) 19262306a36Sopenharmony_ci fmt->code = format->format.code; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci fmt->code = fmt_default.code; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Clamp the colorspace to valid values. */ 19762306a36Sopenharmony_ci fmt->colorspace = format->format.colorspace; 19862306a36Sopenharmony_ci fmt->ycbcr_enc = format->format.ycbcr_enc; 19962306a36Sopenharmony_ci fmt->quantization = format->format.quantization; 20062306a36Sopenharmony_ci fmt->xfer_func = format->format.xfer_func; 20162306a36Sopenharmony_ci vimc_colorimetry_clamp(fmt); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Clamp and align the width and height */ 20562306a36Sopenharmony_ci fmt->width = clamp_t(u32, format->format.width, VIMC_FRAME_MIN_WIDTH, 20662306a36Sopenharmony_ci VIMC_FRAME_MAX_WIDTH) & ~1; 20762306a36Sopenharmony_ci fmt->height = clamp_t(u32, format->format.height, VIMC_FRAME_MIN_HEIGHT, 20862306a36Sopenharmony_ci VIMC_FRAME_MAX_HEIGHT) & ~1; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Propagate the sink pad format to the crop rectangle and the source 21262306a36Sopenharmony_ci * pad. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci if (format->pad == VIMC_SCALER_SINK) { 21562306a36Sopenharmony_ci struct v4l2_mbus_framefmt *src_fmt; 21662306a36Sopenharmony_ci struct v4l2_rect *crop; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which); 21962306a36Sopenharmony_ci crop->width = fmt->width; 22062306a36Sopenharmony_ci crop->height = fmt->height; 22162306a36Sopenharmony_ci crop->top = 0; 22262306a36Sopenharmony_ci crop->left = 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC, 22562306a36Sopenharmony_ci format->which); 22662306a36Sopenharmony_ci *src_fmt = *fmt; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci format->format = *fmt; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int vimc_scaler_get_selection(struct v4l2_subdev *sd, 23562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 23662306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); 23962306a36Sopenharmony_ci struct v4l2_mbus_framefmt *sink_fmt; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (VIMC_IS_SRC(sel->pad)) 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci switch (sel->target) { 24562306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 24662306a36Sopenharmony_ci sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 24962306a36Sopenharmony_ci sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, 25062306a36Sopenharmony_ci sel->which); 25162306a36Sopenharmony_ci sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt); 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void vimc_scaler_adjust_sink_crop(struct v4l2_rect *r, 26162306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *sink_fmt) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci const struct v4l2_rect sink_rect = 26462306a36Sopenharmony_ci vimc_scaler_get_crop_bound_sink(sink_fmt); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Disallow rectangles smaller than the minimal one. */ 26762306a36Sopenharmony_ci v4l2_rect_set_min_size(r, &crop_rect_min); 26862306a36Sopenharmony_ci v4l2_rect_map_inside(r, &sink_rect); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int vimc_scaler_set_selection(struct v4l2_subdev *sd, 27262306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 27362306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); 27662306a36Sopenharmony_ci struct v4l2_mbus_framefmt *sink_fmt; 27762306a36Sopenharmony_ci struct v4l2_rect *crop_rect; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Only support setting the crop of the sink pad */ 28062306a36Sopenharmony_ci if (VIMC_IS_SRC(sel->pad) || sel->target != V4L2_SEL_TGT_CROP) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) 28462306a36Sopenharmony_ci return -EBUSY; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci crop_rect = vimc_scaler_pad_crop(vscaler, sd_state, sel->which); 28762306a36Sopenharmony_ci sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, 28862306a36Sopenharmony_ci sel->which); 28962306a36Sopenharmony_ci vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt); 29062306a36Sopenharmony_ci *crop_rect = sel->r; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return 0; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { 29662306a36Sopenharmony_ci .init_cfg = vimc_scaler_init_cfg, 29762306a36Sopenharmony_ci .enum_mbus_code = vimc_scaler_enum_mbus_code, 29862306a36Sopenharmony_ci .enum_frame_size = vimc_scaler_enum_frame_size, 29962306a36Sopenharmony_ci .get_fmt = vimc_scaler_get_fmt, 30062306a36Sopenharmony_ci .set_fmt = vimc_scaler_set_fmt, 30162306a36Sopenharmony_ci .get_selection = vimc_scaler_get_selection, 30262306a36Sopenharmony_ci .set_selection = vimc_scaler_set_selection, 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (enable) { 31062306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 31162306a36Sopenharmony_ci unsigned int frame_size; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (vscaler->src_frame) 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* Save the bytes per pixel of the sink */ 31762306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code); 31862306a36Sopenharmony_ci vscaler->bpp = vpix->bpp; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Calculate the frame size of the source pad */ 32162306a36Sopenharmony_ci frame_size = vscaler->fmt[VIMC_SCALER_SRC].width 32262306a36Sopenharmony_ci * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Allocate the frame buffer. Use vmalloc to be able to 32562306a36Sopenharmony_ci * allocate a large amount of memory 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci vscaler->src_frame = vmalloc(frame_size); 32862306a36Sopenharmony_ci if (!vscaler->src_frame) 32962306a36Sopenharmony_ci return -ENOMEM; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci if (!vscaler->src_frame) 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci vfree(vscaler->src_frame); 33662306a36Sopenharmony_ci vscaler->src_frame = NULL; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops vimc_scaler_video_ops = { 34362306a36Sopenharmony_ci .s_stream = vimc_scaler_s_stream, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct v4l2_subdev_ops vimc_scaler_ops = { 34762306a36Sopenharmony_ci .pad = &vimc_scaler_pad_ops, 34862306a36Sopenharmony_ci .video = &vimc_scaler_video_ops, 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, 35262306a36Sopenharmony_ci const u8 *const sink_frame) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *src_fmt = &vscaler->fmt[VIMC_SCALER_SRC]; 35562306a36Sopenharmony_ci const struct v4l2_rect *r = &vscaler->crop_rect; 35662306a36Sopenharmony_ci unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width; 35762306a36Sopenharmony_ci unsigned int src_x, src_y; 35862306a36Sopenharmony_ci u8 *walker = vscaler->src_frame; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Set each pixel at the src_frame to its sink_frame equivalent */ 36162306a36Sopenharmony_ci for (src_y = 0; src_y < src_fmt->height; src_y++) { 36262306a36Sopenharmony_ci unsigned int snk_y, y_offset; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci snk_y = (src_y * r->height) / src_fmt->height + r->top; 36562306a36Sopenharmony_ci y_offset = snk_y * snk_width * vscaler->bpp; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci for (src_x = 0; src_x < src_fmt->width; src_x++) { 36862306a36Sopenharmony_ci unsigned int snk_x, x_offset, index; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci snk_x = (src_x * r->width) / src_fmt->width + r->left; 37162306a36Sopenharmony_ci x_offset = snk_x * vscaler->bpp; 37262306a36Sopenharmony_ci index = y_offset + x_offset; 37362306a36Sopenharmony_ci memcpy(walker, &sink_frame[index], vscaler->bpp); 37462306a36Sopenharmony_ci walker += vscaler->bpp; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void *vimc_scaler_process_frame(struct vimc_ent_device *ved, 38062306a36Sopenharmony_ci const void *sink_frame) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device, 38362306a36Sopenharmony_ci ved); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* If the stream in this node is not active, just return */ 38662306a36Sopenharmony_ci if (!vscaler->src_frame) 38762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci vimc_scaler_fill_src_frame(vscaler, sink_frame); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return vscaler->src_frame; 39262306a36Sopenharmony_ci}; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void vimc_scaler_release(struct vimc_ent_device *ved) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct vimc_scaler_device *vscaler = 39762306a36Sopenharmony_ci container_of(ved, struct vimc_scaler_device, ved); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci media_entity_cleanup(vscaler->ved.ent); 40062306a36Sopenharmony_ci kfree(vscaler); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, 40462306a36Sopenharmony_ci const char *vcfg_name) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; 40762306a36Sopenharmony_ci struct vimc_scaler_device *vscaler; 40862306a36Sopenharmony_ci int ret; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Allocate the vscaler struct */ 41162306a36Sopenharmony_ci vscaler = kzalloc(sizeof(*vscaler), GFP_KERNEL); 41262306a36Sopenharmony_ci if (!vscaler) 41362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Initialize ved and sd */ 41662306a36Sopenharmony_ci vscaler->pads[VIMC_SCALER_SINK].flags = MEDIA_PAD_FL_SINK; 41762306a36Sopenharmony_ci vscaler->pads[VIMC_SCALER_SRC].flags = MEDIA_PAD_FL_SOURCE; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev, 42062306a36Sopenharmony_ci vcfg_name, 42162306a36Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, 42262306a36Sopenharmony_ci vscaler->pads, &vimc_scaler_ops); 42362306a36Sopenharmony_ci if (ret) { 42462306a36Sopenharmony_ci kfree(vscaler); 42562306a36Sopenharmony_ci return ERR_PTR(ret); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci vscaler->ved.process_frame = vimc_scaler_process_frame; 42962306a36Sopenharmony_ci vscaler->ved.dev = vimc->mdev.dev; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Initialize the frame format */ 43262306a36Sopenharmony_ci vscaler->fmt[VIMC_SCALER_SINK] = fmt_default; 43362306a36Sopenharmony_ci vscaler->fmt[VIMC_SCALER_SRC] = fmt_default; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Initialize the crop selection */ 43662306a36Sopenharmony_ci vscaler->crop_rect = crop_rect_default; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return &vscaler->ved; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistruct vimc_ent_type vimc_scaler_type = { 44262306a36Sopenharmony_ci .add = vimc_scaler_add, 44362306a36Sopenharmony_ci .release = vimc_scaler_release 44462306a36Sopenharmony_ci}; 445