Received: by 2002:a05:7412:98c1:b0:fa:551:50a7 with SMTP id kc1csp1517939rdb; Mon, 8 Jan 2024 01:30:38 -0800 (PST) X-Google-Smtp-Source: AGHT+IGr7KtGFt6oVL4r56WXhwljFC3SbMCkNp/tXXeYGTCvL6GUbyfthkp9mSGNL40C+mHV9Mqb X-Received: by 2002:a17:90a:d149:b0:28c:6a23:b4c9 with SMTP id t9-20020a17090ad14900b0028c6a23b4c9mr727247pjw.98.1704706237995; Mon, 08 Jan 2024 01:30:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1704706237; cv=none; d=google.com; s=arc-20160816; b=0NgJBKVMmEUjo8k41bPMPHR81NRqS7m3eMC8dRyMRnRWZnqOVA6I+QZs5TwLEXjpb3 3QI2Zpl2WDbWm1uYbbQHT46JYgjNiZvzKFuMs2fl+YnxJd+ashcwUR1Q2j7mz+AzL09z tvPG0myOJxCNR+BhqysyMDrkAT1Iz0SGxh3DgcgMtE1P/N3v5/EEDldpKEG1uam+os3P uKzFIcgQ+8IHhSDgPr/yXTv7GO2qze2hohrgbe+ZXhLgvkxsdPg7691ZO/jl4xRQ/hmT QOmTCO5grts/kWUzGNszOfHT3lYNbzdh6ZfLlj+c+T5qQuudS/Y0EQbxXjdAejOzzKkZ gGfw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=in-reply-to:content-disposition:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:message-id:subject:cc :to:from:date:dkim-signature; bh=z7wY6o+jC+e1FobDMLUjEb2cHKKQf05hAHx4r4B1gU4=; fh=WSz4BOJl7OfmO9o9TDbX0VLL3iB4WlAeV96AEUbIXNU=; b=Xwn9W1GEpSBQO7jOuYzYia7fJ2vuAxJaHQmfgTO5SR8a1VGnrMgnDEhauGWilY/YCz xAMVvCTLbb8prvjfBTnb8GrvR5j1RwO9G+96xNQGyaapAUBIV/qBPhUlXk9hl68iXgvn zqJqks4VSHucPBrYaG3ik0rkS4aItbivjfDKG8sHu8XrZzGuMu2oZpPqYBueJczCzo4b XTDcn8+PDfpgPnMleG7yngh4NiA0NHNzdvEN/AYlbwKI8Om00TcD/eVwUyD6kRoBoW0g H3Apw3PbAcurQxHeiWAZMA/XHwa9Bb8rO+S1pG4uwwLLzb/eYPcWQDAOgCLQW88Zevkz NcMw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b="T7mF5F/w"; spf=pass (google.com: domain of linux-kernel+bounces-19261-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-19261-linux.lists.archive=gmail.com@vger.kernel.org" Return-Path: Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org. [2604:1380:45e3:2400::1]) by mx.google.com with ESMTPS id gx1-20020a17090b124100b0028ce5cdc29esi5259459pjb.177.2024.01.08.01.30.37 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 08 Jan 2024 01:30:37 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-19261-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) client-ip=2604:1380:45e3:2400::1; Authentication-Results: mx.google.com; dkim=pass (test mode) header.i=@ideasonboard.com header.s=mail header.b="T7mF5F/w"; spf=pass (google.com: domain of linux-kernel+bounces-19261-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-19261-linux.lists.archive=gmail.com@vger.kernel.org" Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id 2BC7E2838A5 for ; Mon, 8 Jan 2024 09:19:58 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C3E6D1173D; Mon, 8 Jan 2024 09:19:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="T7mF5F/w" X-Original-To: linux-kernel@vger.kernel.org Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C5E3F1170B; Mon, 8 Jan 2024 09:19:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=ideasonboard.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ideasonboard.com Received: from ideasonboard.com (unknown [IPv6:2001:b07:5d2e:52c9:cc1e:e404:491f:e6ea]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3FDB9552; Mon, 8 Jan 2024 10:18:35 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1704705515; bh=zUD5PLhJbcZWl3zLwbDJ8KqLFUNntLO7Yn6Fb2k7xek=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=T7mF5F/wiyYMv45XZqD7ohCokDD5l+a4nYhZZJ/0jEmUYagtZNiFTPNmw9mwVi5zU TPLgixRvXrm9qq9IfXm1QHmg+ITf05yCy8x+zvRxADrcSwHTQ6UaS/yVrWMzxG/ufT dTSkwtoz1uL0Ind6dgs4s2G0UdXlmdbUiADSAe6M= Date: Mon, 8 Jan 2024 10:19:35 +0100 From: Jacopo Mondi To: Sakari Ailus , Laurent Pinchart Cc: Vinay Varma , Dave Stevenson , Mauro Carvalho Chehab , "open list:SONY IMX219 SENSOR DRIVER" , open list Subject: Re: [PATCH] media: i2c: imx219: implement the v4l2 selection api Message-ID: <3q6andka2su7i43xz2ok44ejvtb3hdjdn6xretyde7sdcvtd7l@lz2syngckivi> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: Hi Sakari, Vinay, a more foundamental question is how this usage of the crop/compose API plays with the fact we enumerate only a limited set of frame sizes, and now you can get an arbitrary output size. We could get away by modifying enum_frame_sizes to return a size range (or ranges) but I wonder if it wouldn't be better to introduce an internal pad to represent the pixel array where to apply TGT_CROP in combination with a source pad where we could apply TGT_COMPOSE and an output format. To be completely honest, binning should be handled with a different mechanism than the selection API, but that would require a completly new design. Laurent: are there plans to introduce internal pad for embedded data support for this sensor ? Also, the patch doesn't apply on the most recent media master, please rebase before resending. On Mon, Jan 08, 2024 at 08:44:59AM +0000, Sakari Ailus wrote: > Hi Vinay, > > Thanks for the patch. > > On Sun, Jan 07, 2024 at 03:42:59PM +0800, Vinay Varma wrote: > > This patch exposes IMX219's crop and compose capabilities > > by implementing the selection API. Horizontal and vertical > > binning being separate registers, `imx219_binning_goodness` > > computes the best possible height and width of the compose > > specification using the selection flags. Compose operation > > updates the subdev's format object to keep them in sync. > > The line length limit here is 75, not ~ 60. Please rewrap. > > > > > Signed-off-by: Vinay Varma > > --- > > drivers/media/i2c/imx219.c | 222 +++++++++++++++++++++++++++++++------ > > 1 file changed, 190 insertions(+), 32 deletions(-) > > > > diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c > > index 39943d72c22d..27d85fb7ad51 100644 > > --- a/drivers/media/i2c/imx219.c > > +++ b/drivers/media/i2c/imx219.c > > @@ -29,6 +29,7 @@ > > #include > > #include > > #include > > +#include > > > > /* Chip ID */ > > #define IMX219_REG_CHIP_ID CCI_REG16(0x0000) > > @@ -73,6 +74,7 @@ > > /* V_TIMING internal */ > > #define IMX219_REG_VTS CCI_REG16(0x0160) > > #define IMX219_VTS_MAX 0xffff > > +#define IMX219_VTS_DEF 1763 > > > > #define IMX219_VBLANK_MIN 32 > > > > @@ -146,6 +148,7 @@ > > #define IMX219_PIXEL_ARRAY_TOP 8U > > #define IMX219_PIXEL_ARRAY_WIDTH 3280U > > #define IMX219_PIXEL_ARRAY_HEIGHT 2464U > > +#define IMX219_MIN_COMPOSE_SIZE 8U > > Please align 8U with the rest of the macros. Same above. > > > > > /* Mode : resolution and related config&values */ > > struct imx219_mode { > > @@ -284,6 +287,8 @@ static const u32 imx219_mbus_formats[] = { > > #define IMX219_XCLR_MIN_DELAY_US 6200 > > #define IMX219_XCLR_DELAY_RANGE_US 1000 > > > > +static const u32 binning_ratios[] = { 1, 2 }; > > + > > /* Mode configs */ > > static const struct imx219_mode supported_modes[] = { > > { > > @@ -296,19 +301,19 @@ static const struct imx219_mode supported_modes[] = { > > /* 1080P 30fps cropped */ > > .width = 1920, > > .height = 1080, > > - .vts_def = 1763, > > + .vts_def = IMX219_VTS_DEF, > > }, > > { > > /* 2x2 binned 30fps mode */ > > .width = 1640, > > .height = 1232, > > - .vts_def = 1763, > > + .vts_def = IMX219_VTS_DEF, > > }, > > { > > /* 640x480 30fps mode */ > > .width = 640, > > .height = 480, > > - .vts_def = 1763, > > + .vts_def = IMX219_VTS_DEF, > > }, > > }; > > > > @@ -809,6 +814,39 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd, > > return 0; > > } > > > > +static void imx219_refresh_ctrls(struct v4l2_subdev *sd, > > + struct v4l2_subdev_state *state, > > + unsigned int vts_def) > > +{ > > + int exposure_max; > > + int exposure_def; > > + int hblank; > > + struct imx219 *imx219 = to_imx219(sd); > > + struct v4l2_mbus_framefmt *fmt = > > + v4l2_subdev_get_pad_format(sd, state, 0); > > + > > + /* Update limits and set FPS to default */ > > + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, > > + IMX219_VTS_MAX - fmt->height, 1, > > + vts_def - fmt->height); > > + __v4l2_ctrl_s_ctrl(imx219->vblank, vts_def - fmt->height); > > + /* Update max exposure while meeting expected vblanking */ > > + exposure_max = vts_def - 4; > > + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? > > + exposure_max : > > + IMX219_EXPOSURE_DEFAULT; > > + __v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum, > > + exposure_max, imx219->exposure->step, > > + exposure_def); > > + /* > > + * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank > > + * depends on mode->width only, and is not changeble in any > > + * way other than changing the mode. > > + */ > > + hblank = IMX219_PPL_DEFAULT - fmt->width; > > + __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank); > > +} > > + > > static int imx219_set_pad_format(struct v4l2_subdev *sd, > > struct v4l2_subdev_state *state, > > struct v4l2_subdev_format *fmt) > > @@ -816,7 +854,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, > > struct imx219 *imx219 = to_imx219(sd); > > const struct imx219_mode *mode; > > struct v4l2_mbus_framefmt *format; > > - struct v4l2_rect *crop; > > + struct v4l2_rect *crop, *compose; > > unsigned int bin_h, bin_v; > > > > mode = v4l2_find_nearest_size(supported_modes, > > @@ -842,34 +880,14 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, > > crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2; > > crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2; > > > > - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { > > - int exposure_max; > > - int exposure_def; > > - int hblank; > > - > > - /* Update limits and set FPS to default */ > > - __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, > > - IMX219_VTS_MAX - mode->height, 1, > > - mode->vts_def - mode->height); > > - __v4l2_ctrl_s_ctrl(imx219->vblank, > > - mode->vts_def - mode->height); > > - /* Update max exposure while meeting expected vblanking */ > > - exposure_max = mode->vts_def - 4; > > - exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? > > - exposure_max : IMX219_EXPOSURE_DEFAULT; > > - __v4l2_ctrl_modify_range(imx219->exposure, > > - imx219->exposure->minimum, > > - exposure_max, imx219->exposure->step, > > - exposure_def); > > - /* > > - * Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank > > - * depends on mode->width only, and is not changeble in any > > - * way other than changing the mode. > > - */ > > - hblank = IMX219_PPL_DEFAULT - mode->width; > > - __v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, > > - hblank); > > - } > > + compose = v4l2_subdev_get_pad_compose(sd, state, 0); > > + compose->width = format->width; > > + compose->height = format->height; > > + compose->left = 0; > > + compose->top = 0; > > + > > + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) > > + imx219_refresh_ctrls(sd, state, mode->vts_def); > > > > return 0; > > } > > @@ -884,6 +902,11 @@ static int imx219_get_selection(struct v4l2_subdev *sd, > > return 0; > > } > > > > + case V4L2_SEL_TGT_COMPOSE: { > > + sel->r = *v4l2_subdev_get_pad_compose(sd, state, 0); > > + return 0; > > + } > > The braces are unnecessary here. > > > + > > case V4L2_SEL_TGT_NATIVE_SIZE: > > sel->r.top = 0; > > sel->r.left = 0; > > @@ -900,11 +923,145 @@ static int imx219_get_selection(struct v4l2_subdev *sd, > > sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; > > > > return 0; > > + > > + case V4L2_SEL_TGT_COMPOSE_DEFAULT: > > + case V4L2_SEL_TGT_COMPOSE_BOUNDS: > > + case V4L2_SEL_TGT_COMPOSE_PADDED: > > + sel->r.top = 0; > > + sel->r.left = 0; > > + sel->r.width = IMX219_PIXEL_ARRAY_WIDTH; > > + sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT; > > + return 0; > > } > > > > return -EINVAL; > > } > > > > +#define IMX219_ROUND(dim, step, flags) \ > > + ((flags) & V4L2_SEL_FLAG_GE ? \ > > + round_up((dim), (step)) : \ > > + ((flags) & V4L2_SEL_FLAG_LE ? \ > > + round_down((dim), (step)) : \ > > + round_down((dim) + (step) / 2, (step)))) > > + > > +static int imx219_set_selection_crop(struct v4l2_subdev *sd, > > + struct v4l2_subdev_state *sd_state, > > + struct v4l2_subdev_selection *sel) > > +{ > > + u32 max_binning; > > + struct v4l2_rect *compose, *crop; > > + struct v4l2_mbus_framefmt *fmt; > > + > > + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); > > + if (v4l2_rect_equal(&sel->r, crop)) > > + return false; > > + max_binning = binning_ratios[ARRAY_SIZE(binning_ratios) - 1]; > > + sel->r.width = > > + clamp(IMX219_ROUND(sel->r.width, max_binning, sel->flags), > > + max_binning * IMX219_MIN_COMPOSE_SIZE, > > + IMX219_PIXEL_ARRAY_WIDTH); > > + sel->r.height = > > + clamp(IMX219_ROUND(sel->r.width, max_binning, sel->flags), > > + max_binning * IMX219_MIN_COMPOSE_SIZE, > > + IMX219_PIXEL_ARRAY_WIDTH); > > + sel->r.left = > > + min_t(u32, sel->r.left, IMX219_PIXEL_ARRAY_LEFT - sel->r.width); > > + sel->r.top = > > + min_t(u32, sel->r.top, IMX219_PIXEL_ARRAY_TOP - sel->r.top); > > + > > + compose = v4l2_subdev_get_pad_compose(sd, sd_state, 0); > > + fmt = v4l2_subdev_get_pad_format(sd, sd_state, 0); > > + *crop = sel->r; > > + compose->height = crop->height; > > + compose->width = crop->width; > > + return true; > > +} > > + > > +static int imx219_binning_goodness(u32 act, u32 ask, u32 flags) > > +{ > > + const int goodness = 100000; > > + int val = 0; > > + > > + if (flags & V4L2_SEL_FLAG_GE) > > + if (act < ask) > > + val -= goodness; > > + > > + if (flags & V4L2_SEL_FLAG_LE) > > + if (act > ask) > > + val -= goodness; > > + > > + val -= abs(act - ask); > > + > > + return val; > > +} > > + > > +static bool imx219_set_selection_compose(struct v4l2_subdev *sd, > > + struct v4l2_subdev_state *state, > > + struct v4l2_subdev_selection *sel) > > +{ > > + int best_goodness; > > This would be nicer if declared after the line below. Think of reverse > Christmas trees. > > Similarly for max_binning a few functions up actually as well as variables > in imx219_refresh_ctrls(). > > > + struct v4l2_rect *compose, *crop; > > + > > + compose = v4l2_subdev_get_pad_compose(sd, state, 0); > > + if (v4l2_rect_equal(compose, &sel->r)) > > + return false; > > + > > + crop = v4l2_subdev_get_pad_crop(sd, state, 0); > > + > > + best_goodness = INT_MIN; > > + for (int i = 0; i < ARRAY_SIZE(binning_ratios); ++i) { > > + u32 width = crop->width / binning_ratios[i]; > > + int goodness = imx219_binning_goodness(width, sel->r.width, > > + sel->flags); > > + if (goodness > best_goodness) { > > + best_goodness = goodness; > > + compose->width = width; > > + } > > + } > > + best_goodness = INT_MIN; > > + for (int i = 0; i < ARRAY_SIZE(binning_ratios); ++i) { > > + u32 height = crop->height / binning_ratios[i]; > > + int goodness = imx219_binning_goodness(height, sel->r.height, > > + sel->flags); > > + if (goodness > best_goodness) { > > + best_goodness = goodness; > > + compose->height = height; > > + } > > + } > > + return true; > > +} > > + > > +static int imx219_set_selection(struct v4l2_subdev *sd, > > + struct v4l2_subdev_state *sd_state, > > + struct v4l2_subdev_selection *sel) > > +{ > > + bool compose_updated = false; > > + > > + switch (sel->target) { > > + case V4L2_SEL_TGT_CROP: > > + compose_updated = imx219_set_selection_crop(sd, sd_state, sel); > > + break; > > + case V4L2_SEL_TGT_COMPOSE: > > + compose_updated = > > + imx219_set_selection_compose(sd, sd_state, sel); > > + break; > > + default: > > + return -EINVAL; > > + } > > + if (compose_updated) { > > + struct v4l2_rect *compose = > > + v4l2_subdev_get_pad_compose(sd, sd_state, 0); > > + struct v4l2_mbus_framefmt *fmt = > > + v4l2_subdev_get_pad_format(sd, sd_state, 0); > > A newline here? > > > + fmt->height = compose->height; > > + fmt->width = compose->width; > > + } > > + if (compose_updated && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) > > + imx219_refresh_ctrls(sd, sd_state, IMX219_VTS_DEF); > > Please move this inside the previous condition (where you check just > sel->which). > > > + > > + return 0; > > +} > > + > > static int imx219_init_cfg(struct v4l2_subdev *sd, > > struct v4l2_subdev_state *state) > > { > > @@ -938,6 +1095,7 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = { > > .get_fmt = v4l2_subdev_get_fmt, > > .set_fmt = imx219_set_pad_format, > > .get_selection = imx219_get_selection, > > + .set_selection = imx219_set_selection, > > .enum_frame_size = imx219_enum_frame_size, > > }; > > > > -- > Regards, > > Sakari Ailus >