Hi Eugen On Fri, Feb 11, 2022 at 12:32:14PM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > On 2/11/22 1:55 PM, Jacopo Mondi wrote: > > Hi Eugen > > > > On Fri, Feb 11, 2022 at 10:32:43AM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > >> On 2/10/22 7:49 PM, Jacopo Mondi wrote: > >>> Hi Eugen > >>> > >>> On Wed, Feb 09, 2022 at 06:59:37AM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > >>>> On 2/8/22 9:30 PM, Jacopo Mondi wrote: > >>>>> Hi Eugen > >>>>> > >>>>> On Tue, Feb 08, 2022 at 10:33:31AM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > >>>>>> On 2/8/22 10:59 AM, Jacopo Mondi wrote: > >>>>>>> Hi Eugen > >>>>>>> > >>>>>>> On Mon, Feb 07, 2022 at 01:40:31PM +0000, Eugen.Hristev@xxxxxxxxxxxxx wrote: > >>>>>>>> On 2/7/22 2:44 PM, Jacopo Mondi wrote: > >>>>>>>>> Hi Eugen > >>>>>>>>> > >>>>>>>>> On Fri, Jan 21, 2022 at 03:14:08PM +0200, Eugen Hristev wrote: > >>>>>>>>>> Implement the support for media-controller. > >>>>>>>>>> This means that the capabilities of the driver have changed and now > >>>>>>>>>> it also advertises the IO_MC . > >>>>>>>>>> The driver will register it's media device, and add the video entity to this > >>>>>>>>>> media device. The subdevices are registered to the same media device. > >>>>>>>>>> The ISC will have a base entity which is auto-detected as atmel_isc_base. > >>>>>>>>>> It will also register a subdevice that allows cropping of the incoming frame > >>>>>>>>>> to the maximum frame size supported by the ISC. > >>>>>>>>>> The ISC will create a link between the subdevice that is asynchronously > >>>>>>>>>> registered and the atmel_isc_scaler entity. > >>>>>>>>>> Then, the atmel_isc_scaler and atmel_isc_base are connected through another > >>>>>>>>>> link. > >>>>>>>>>> > >>>>>>>>>> Signed-off-by: Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx> > >>>>>>>>>> --- > >>>>>>>>>> Changes in v4: > >>>>>>>>>> As suggested by Jacopo: > >>>>>>>>>> - renamed atmel_isc_mc to atmel_isc_scaler.c > >>>>>>>>>> - moved init_mc/clean_mc to isc_base file > >>>>>>>>>> > >>>>>>>>>> Changes in v2: > >>>>>>>>>> - implement try formats > >>>>>>>>>> > >>>>>>>>>> drivers/media/platform/atmel/Makefile | 2 +- > >>>>>>>>>> drivers/media/platform/atmel/atmel-isc-base.c | 73 +++++- > >>>>>>>>>> .../media/platform/atmel/atmel-isc-scaler.c | 245 ++++++++++++++++++ > >>>>>>>>>> drivers/media/platform/atmel/atmel-isc.h | 37 +++ > >>>>>>>>>> .../media/platform/atmel/atmel-sama5d2-isc.c | 14 +- > >>>>>>>>>> .../media/platform/atmel/atmel-sama7g5-isc.c | 12 +- > >>>>>>>>>> 6 files changed, 375 insertions(+), 8 deletions(-) > >>>>>>>>>> create mode 100644 drivers/media/platform/atmel/atmel-isc-scaler.c > >>>>>>>>>> > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile > >>>>>>>>>> index 794e8f739287..f02d03df89d6 100644 > >>>>>>>>>> --- a/drivers/media/platform/atmel/Makefile > >>>>>>>>>> +++ b/drivers/media/platform/atmel/Makefile > >>>>>>>>>> @@ -1,7 +1,7 @@ > >>>>>>>>>> # SPDX-License-Identifier: GPL-2.0-only > >>>>>>>>>> atmel-isc-objs = atmel-sama5d2-isc.o > >>>>>>>>>> atmel-xisc-objs = atmel-sama7g5-isc.o > >>>>>>>>>> -atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o > >>>>>>>>>> +atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o atmel-isc-scaler.o > >>>>>>>>>> > >>>>>>>>>> obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o > >>>>>>>>>> obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c > >>>>>>>>>> index 6b0005987a17..6b482270eb93 100644 > >>>>>>>>>> --- a/drivers/media/platform/atmel/atmel-isc-base.c > >>>>>>>>>> +++ b/drivers/media/platform/atmel/atmel-isc-base.c > >>>>>>>>>> @@ -1710,6 +1710,7 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier, > >>>>>>>>>> struct isc_device, v4l2_dev); > >>>>>>>>>> struct isc_subdev_entity *subdev_entity = > >>>>>>>>>> container_of(notifier, struct isc_subdev_entity, notifier); > >>>>>>>>>> + int pad; > >>>>>>>>>> > >>>>>>>>>> if (video_is_registered(&isc->video_dev)) { > >>>>>>>>>> v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n"); > >>>>>>>>>> @@ -1718,6 +1719,16 @@ static int isc_async_bound(struct v4l2_async_notifier *notifier, > >>>>>>>>>> > >>>>>>>>>> subdev_entity->sd = subdev; > >>>>>>>>>> > >>>>>>>>>> + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, > >>>>>>>>>> + MEDIA_PAD_FL_SOURCE); > >>>>>>>>>> + if (pad < 0) { > >>>>>>>>>> + v4l2_err(&isc->v4l2_dev, "failed to find pad for %s\n", > >>>>>>>>>> + subdev->name); > >>>>>>>>>> + return pad; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + isc->remote_pad = pad; > >>>>>>>>>> + > >>>>>>>>>> return 0; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> @@ -1732,8 +1743,8 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier, > >>>>>>>>>> v4l2_ctrl_handler_free(&isc->ctrls.handler); > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> -static struct isc_format *find_format_by_code(struct isc_device *isc, > >>>>>>>>>> - unsigned int code, int *index) > >>>>>>>>>> +struct isc_format *isc_find_format_by_code(struct isc_device *isc, > >>>>>>>>>> + unsigned int code, int *index) > >>>>>>>>>> { > >>>>>>>>>> struct isc_format *fmt = &isc->formats_list[0]; > >>>>>>>>>> unsigned int i; > >>>>>>>>>> @@ -1749,6 +1760,7 @@ static struct isc_format *find_format_by_code(struct isc_device *isc, > >>>>>>>>>> > >>>>>>>>>> return NULL; > >>>>>>>>>> } > >>>>>>>>>> +EXPORT_SYMBOL_GPL(isc_find_format_by_code); > >>>>>>>>>> > >>>>>>>>>> static int isc_formats_init(struct isc_device *isc) > >>>>>>>>>> { > >>>>>>>>>> @@ -1765,7 +1777,7 @@ static int isc_formats_init(struct isc_device *isc) > >>>>>>>>>> NULL, &mbus_code)) { > >>>>>>>>>> mbus_code.index++; > >>>>>>>>>> > >>>>>>>>>> - fmt = find_format_by_code(isc, mbus_code.code, &i); > >>>>>>>>>> + fmt = isc_find_format_by_code(isc, mbus_code.code, &i); > >>>>>>>>>> if (!fmt) { > >>>>>>>>>> v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n", > >>>>>>>>>> mbus_code.code); > >>>>>>>>>> @@ -1891,7 +1903,8 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) > >>>>>>>>>> vdev->queue = q; > >>>>>>>>>> vdev->lock = &isc->lock; > >>>>>>>>>> vdev->ctrl_handler = &isc->ctrls.handler; > >>>>>>>>>> - vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; > >>>>>>>>>> + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | > >>>>>>>>>> + V4L2_CAP_IO_MC; > >>>>>>>>>> video_set_drvdata(vdev, isc); > >>>>>>>>>> > >>>>>>>>>> ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); > >>>>>>>>>> @@ -1901,8 +1914,18 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) > >>>>>>>>>> goto isc_async_complete_err; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> + ret = isc_scaler_link(isc); > >>>>>>>>>> + if (ret < 0) > >>>>>>>>>> + goto isc_async_complete_unregister_device; > >>>>>>>>>> + > >>>>>>>>>> + ret = media_device_register(&isc->mdev); > >>>>>>>>>> + if (ret < 0) > >>>>>>>>>> + goto isc_async_complete_unregister_device; > >>>>>>>>>> return 0; > >>>>>>>>>> > >>>>>>>>>> +isc_async_complete_unregister_device: > >>>>>>>>>> + video_unregister_device(vdev); > >>>>>>>>>> + > >>>>>>>>>> isc_async_complete_err: > >>>>>>>>>> mutex_destroy(&isc->lock); > >>>>>>>>>> return ret; > >>>>>>>>>> @@ -1969,6 +1992,48 @@ int isc_pipeline_init(struct isc_device *isc) > >>>>>>>>>> } > >>>>>>>>>> EXPORT_SYMBOL_GPL(isc_pipeline_init); > >>>>>>>>>> > >>>>>>>>>> +int isc_mc_init(struct isc_device *isc, u32 ver) > >>>>>>>>>> +{ > >>>>>>>>>> + const struct of_device_id *match; > >>>>>>>>>> + int ret; > >>>>>>>>>> + > >>>>>>>>>> + isc->video_dev.entity.function = MEDIA_ENT_F_IO_V4L; > >>>>>>>>>> + isc->video_dev.entity.flags = MEDIA_ENT_FL_DEFAULT; > >>>>>>>>> > >>>>>>>>> Should you set entity.ops.link_validate = v4l2_subdev_link_validate > >>>>>>>>> to be able to have the media pipeline validated at > >>>>>>>>> media_pipeline_start() time ? > >>>>>>>> > >>>>>>>> Hi, > >>>>>>>> > >>>>>>>> I am doing that in a subsequent patch. Things are not completely ready > >>>>>>>> at this moment, because ISC still relies on the old mechanism to call > >>>>>>>> v4l2_subdev_call with set_fmt on the subdevice (which subsequent patch > >>>>>>>> removes and adds checks to the link validate ). > >>>>>>>> > >>>>>>> > >>>>>>> I see.. the subsequent patches are not part of this series, right ? > >>>>>> > >>>>>> No. Patch 7 does this. In that patch, the link validation is created and > >>>>>> all the logic of format propagation is removed and reworked, and creates > >>>>>> the need for the link_validate call > >>>>>> Could you have also a look at that patch ? > >>>>> > >>>>> Ah ups, 7 was not in my inbox with the rest of the series. Just > >>>>> noticed. I had even reviewed the previous version, I should have > >>>>> remembered link_validate was added later > >>>>> > >>>>>>> > >>>>>>> I think patches 1, 2, 4, 5 and 6 from this series do not depend on > >>>>>>> the introduction of media controller or the scaler, right ? (I'm not > >>>>>>> sure if the DTS patches do). > >>>>> > >>>>> Do DTS patches depend on media-controller plumbing ? > >>>> > >>>> I think not, but, I have not tested them without the media controller. > >>>> > >>>>> > >>>>>>> > >>>>>>> If that's the case, would it make sense to to fast-track them (as some > >>>>>>> of them are fixes) and then on top plumb the media controller > >>>>>>> infrastructure with this patch and the ones you have been mentioning ? > >>>>>> > >>>>>> It would make sense. > >>>>>> > >>>>> > >>>>> Once the other discussion with Hans about the stop state it might make > >>>>> sense to fastrack the first part of the series ? > >>>> > >>>> The other three patches (4,5,6) are quite independent and can go faster. > >>>>> > >>>>>>> > >>>>>>>>> > >>>>>>>>>> + isc->pads[ISC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; > >>>>>>>>>> + > >>>>>>>>>> + ret = media_entity_pads_init(&isc->video_dev.entity, ISC_PADS_NUM, > >>>>>>>>>> + isc->pads); > >>>>>>>>>> + if (ret < 0) { > >>>>>>>>>> + dev_err(isc->dev, "media entity init failed\n"); > >>>>>>>>>> + return ret; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + isc->mdev.dev = isc->dev; > >>>>>>>>>> + > >>>>>>>>>> + match = of_match_node(isc->dev->driver->of_match_table, > >>>>>>>>>> + isc->dev->of_node); > >>>>>>>>>> + > >>>>>>>>>> + strscpy(isc->mdev.driver_name, KBUILD_MODNAME, > >>>>>>>>>> + sizeof(isc->mdev.driver_name)); > >>>>>>>>>> + strscpy(isc->mdev.model, match->compatible, sizeof(isc->mdev.model)); > >>>>>>>>>> + snprintf(isc->mdev.bus_info, sizeof(isc->mdev.bus_info), "platform:%s", > >>>>>>>>>> + isc->v4l2_dev.name); > >>>>>>>>>> + isc->mdev.hw_revision = ver; > >>>>>>>>>> + > >>>>>>>>>> + media_device_init(&isc->mdev); > >>>>>>>>>> + > >>>>>>>>>> + isc->v4l2_dev.mdev = &isc->mdev; > >>>>>>>>>> + > >>>>>>>>>> + return isc_scaler_init(isc); > >>>>>>>>>> +} > >>>>>>>>>> +EXPORT_SYMBOL_GPL(isc_mc_init); > >>>>>>>>>> + > >>>>>>>>>> +void isc_mc_cleanup(struct isc_device *isc) > >>>>>>>>>> +{ > >>>>>>>>>> + media_entity_cleanup(&isc->video_dev.entity); > >>>>>>>>>> +} > >>>>>>>>>> +EXPORT_SYMBOL_GPL(isc_mc_cleanup); > >>>>>>>>>> + > >>>>>>>>>> /* regmap configuration */ > >>>>>>>>>> #define ATMEL_ISC_REG_MAX 0xd5c > >>>>>>>>>> const struct regmap_config isc_regmap_config = { > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/atmel-isc-scaler.c b/drivers/media/platform/atmel/atmel-isc-scaler.c > >>>>>>>>>> new file mode 100644 > >>>>>>>>>> index 000000000000..ec95c9665883 > >>>>>>>>>> --- /dev/null > >>>>>>>>>> +++ b/drivers/media/platform/atmel/atmel-isc-scaler.c > >>>>>>>>>> @@ -0,0 +1,245 @@ > >>>>>>>>>> +// SPDX-License-Identifier: GPL-2.0-only > >>>>>>>>>> +/* > >>>>>>>>>> + * Microchip Image Sensor Controller (ISC) Scaler entity support > >>>>>>>>>> + * > >>>>>>>>>> + * Copyright (C) 2021 Microchip Technology, Inc. > >>>>>>>>> > >>>>>>>>> Time flies! It's 2022 already :) > >>>>>>>>> > >>>>>>>>>> + * > >>>>>>>>>> + * Author: Eugen Hristev <eugen.hristev@xxxxxxxxxxxxx> > >>>>>>>>>> + * > >>>>>>>>>> + */ > >>>>>>>>>> + > >>>>>>>>>> +#include <media/media-device.h> > >>>>>>>>>> +#include <media/media-entity.h> > >>>>>>>>>> +#include <media/v4l2-device.h> > >>>>>>>>>> +#include <media/v4l2-subdev.h> > >>>>>>>>>> + > >>>>>>>>>> +#include "atmel-isc-regs.h" > >>>>>>>>>> +#include "atmel-isc.h" > >>>>>>>>>> + > >>>>>>>>>> +static int isc_scaler_get_fmt(struct v4l2_subdev *sd, > >>>>>>>>>> + struct v4l2_subdev_state *sd_state, > >>>>>>>>>> + struct v4l2_subdev_format *format) > >>>>>>>>>> +{ > >>>>>>>>>> + struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); > >>>>>>>>>> + struct v4l2_mbus_framefmt *v4l2_try_fmt; > >>>>>>>>>> + > >>>>>>>>>> + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { > >>>>>>>>>> + v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, > >>>>>>>>>> + format->pad); > >>>>>>>>>> + format->format = *v4l2_try_fmt; > >>>>>>>>>> + > >>>>>>>>>> + return 0; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + format->format = isc->scaler_format; > >>>>>>>>> > >>>>>>>>> isc->scaler_format is only used inside this file if I'm not mistaken. > >>>>>>>>> I wonder why it lives in the isc_device struct. > >>>>>>>> > >>>>>>>> isc_device is a placeholder for all isc things. > >>>>>>>> > >>>>>>>> I would not create a separate struct in this file that would have to be > >>>>>>>> allocated etc... just for one two things. So I preferred to have it in > >>>>>>>> the same place as all the other things. > >>>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + return 0; > >>>>>>>>>> +} > >>>>>>>>>> + > >>>>>>>>>> +static int isc_scaler_set_fmt(struct v4l2_subdev *sd, > >>>>>>>>>> + struct v4l2_subdev_state *sd_state, > >>>>>>>>>> + struct v4l2_subdev_format *req_fmt) > >>>>>>>>>> +{ > >>>>>>>>>> + struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); > >>>>>>>>>> + struct v4l2_mbus_framefmt *v4l2_try_fmt; > >>>>>>>>>> + struct isc_format *fmt; > >>>>>>>>>> + unsigned int i; > >>>>>>>>>> + > >>>>>>>>>> + if (req_fmt->pad == ISC_SCALER_PAD_SOURCE) > >>>>>>>>>> + v4l_bound_align_image > >>>>>>>>>> + (&req_fmt->format.width, 16, isc->max_width, 0, > >>>>>>>>>> + &req_fmt->format.height, 16, isc->max_height, 0, 0); > >>>>>>>>>> + else > >>>>>>>>>> + v4l_bound_align_image > >>>>>>>>>> + (&req_fmt->format.width, 16, 10000, 0, > >>>>>>>>>> + &req_fmt->format.height, 16, 10000, 0, 0); > >>>>>>>>> > >>>>>>>>> Where does 10000 come from ? > >>>>>>>> > >>>>>>>> It's a random number. Do you have any suggestion for a better one ? > >>>>>>> > >>>>>>> An actual HW limit ? :) > >>>>>> > >>>>>> There is no hardware limit. The ISC just stops sampling pixels once the > >>>>>> crop limit is reached. The element that generates pixels could go on > >>>>>> forever , or until the next HBLANK/VBLANK. So what limit could I place > >>>>>> here ? > >>>>>> The cropping is mandatory, otherwise there could be an overflow w.r.t. > >>>>>> the buffer size if the ISC would sample more pixels than the software > >>>>>> expects it to. > >>>>>> > >>>>>>> > >>>>>>>> Maybe this would be much more clear with my comments below (where I will > >>>>>>>> explain what the isc scaler does ) > >>>>>>>> In short, it allows the other entity in the link to 'sink' a huge format > >>>>>>>> into this 'scaler' . Because the scaler can crop anything basically down > >>>>>>>> to the biggest format size the ISC could handle. > >>>>>>>> > >>>>>>> > >>>>>>> doesn't it have any documented input size limit ? > >>>>>> > >>>>>> As stated above, ISC handles a pixel stream and stores it in an internal > >>>>>> SRAM. ISC stops sampling when this SRAM is full or when the cropping > >>>>>> limit is reached. > >>>>>> After storing the pixels in the SRAM (which is limited to the ISC > >>>>>> maximum frame size), the ISC will work on the pixels and then DMA the > >>>>>> frame to another RAM. > >>>>>> > >>>>> > >>>>> I understand... > >>>>> > >>>>> So whatever the input size is, only the first n-th bytes are actually > >>>>> dumped to memory, the following ones are discarded. > >>>> > >>>> Yes ! That's the behavior. > >>>>> > >>>>> So the crop is always applied on the top-leftmost corner as a > >>>>> consequence, right ? > >>>> > >>>> Right. Pixels come in order. The ISC does not really know how many > >>>> pixels are coming until its memory is full (or counting until the crop > >>>> limit is reached). After that it stops sampling pixels, and waits for > >>>> vblank to finish the frame. > >>>> > >>>> > >>>>> > >>>>> I understand why you had difficulties picking a default :) > >>>>> > >>>>> This is not easy to handle, as the scaler doesn't actually applies a > >>>>> crop but rather stops capturing after the destination buffer has been > >>>>> saturated. So the limiting factor is the full image size, not one of > >>>>> its dimensions, like in example the line length. This makes it hard > >>>>> to adjust cropping to something meaningful for most use cases. > >>>> > >>>> I didn't know how to name this except crop. It 'crops out' what does not > >>>> fit in the internal memory of the ISC > >>>> > >>>>> > >>>>> In your below example you suggest that an image size of (800000 x 10) > >>>>> would theoretically be captured correctly. What if the input is of > >>>>> size (800000 x 10 + 1). What size would you set the crop rectangle to ? > >>>>> > >>>>> I wouldn't however be too much concerned for the moment, but I would > >>>>> record this in a comment ? > >>>> > >>>> I discussed a bit more with designer and looked in datasheet. > >>>> It looks like the horizontal line length is not a hard limit, but it > >>>> makes the ISC internal ISP stop working, so I would keep the horizontal > >>>> limit as well for now, unless an important use case (I am coming back to > >>>> that). The vertical limit looks to be a hard requirement. > >>>> So in short, we have to impose a vertical limit of 2464 , we cannot have > >>>> more lines than these. So if the horizontal is 100 pixels e.g., we can > >>>> capture only 100x2464 . > >>>> In practice we could capture [16,3264] on the horizontal, and [16,2464] > >>>> on the vertical, and in theory horizontal could go more like [16, 4000], > >>>> but we have to limit the vertical, and without the internal ISP. > >>>> So I would not haphazardly go in this direction. Let's stick to the > >>>> datasheet maximum frame 3264x2464 . > >>>> According to this , I changed the frame size of the ISC to be a > >>>> continuous size from [16,16] to [3264,2464] > >>>> About that use case that I talked about earlier, we have one sensor , > >>>> imx274, which is more wide, and outputs something like 3800x2100 . > >>>> With the cropping and limitations we have in place now, this will be > >>>> limited to 3264x2100 , which is in line with datasheet and what the ISC > >>>> supports now. > >>>> As in theory we could capture 3800x2100 as long as the SRAM doesn't get > >>>> full, for now I would like to avoid capturing this size (and disable the > >>>> internal ISP), until we have someone really asking for such kind of > >>>> capture and with some good reasons. > >>>> In short I would like to have the crop rectangle to 3264x2464 and the > >>>> bounds would be limited to the incoming frame but no H>3264 and no > >>>> V>2464. Does that make sense ? > >>> > >>> Just to make sure I got it: is the ISC actually able to crop on the > >>> line length ? If you apply a 3264 limit, will the last 536 pixels of > >>> each line will be dropped if the input frame is 3800x2100 as your > >>> above example ? > >> > >> Yes. Last 536 pixels will be dropped. > >> After the 3264 limit, no more pixels are being sampled until HBLANK. > >>> > >>> Or rather, is the SRAM size limit the issue and the lines gets > >>> captured in full lenght no matter what, until the buffer gets filled up > >>> and all the successive pixels dropped. > >>> > >>> In other words, do your cropped image look like: > >>> > >>> 3264 > >>> +--------------------------------X-----------+ 3800 > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | > >>> +--------------------------------------------+ 2100 > >>> > >>> Or rather > >>> > >>> > >>> 3264 > >>> +--------------------------------X-----------+ 3800 > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| > >>> |xxxxxxxxxxxxxxxxx| | > >>> | | > >>> +--------------------------------------------+ 2100 > >>> > >>> I assume the first, as otherwise you wouldn't be able to interpret the > >>> image as 3264x2100. > >> > >> Yes, the first. > >> > >>> > >>> I'm asking because you had the un-cropped 3280x2464 size as the ISC > >>> source pad size in the paste of the media graph you provided below, > >>> which might mean the output image is actually full size in lines > >>> lenght and it's maybe missing pixels at the end, in case the ISC > >>> operates as in the above second case. > >> > >> No, the image won't be padded with blanks on the right side. Actually > >> this cannot happen due to a very big reason: the frame is being written > >> to the main memory by a DMA controller. This DMA controller will write > >> all the lines at consecutive addresses, and it will write exactly how > >> many pixels are being captured by the module from the ISC named > >> 'parallel front end' which does the cropping. Thus, the DMA will never > >> output empty padding bytes at the end of each line, and it will not add > >> a certain stride to each line if not instructed to. In fact the DMA can > >> add a stride offset for the address of each line, which can be used for > >> composing or copying the frame into another frame, but this is a > >> different story. ( I tried this for fun, but not implemented in the > >> driver at the moment) > > > > Oh that will really be interesting and it's indeed an argument for > > hainvg a scaler subdevice indeed > > > >> > >> The cropping is also mandatory even if the captured frame is less than > >> the maximum size, because the ISC has no configuration related to frame > >> size in fact. The parallel front end captures pixels and the dma engine > >> will write them to memory. There is no control over this except the > >> cropping. > >> So for example if the software expects 640x480 but somehow someone sends > >> more pixels, it can lead to buffer overflow if the parallel front end is > >> not configured to ignore(crop) anything beyond 640x480 . > >> > > > > I see and I think you have correctly plumbed that with the scaler sink > > pad crop rectangle. I don't seem to find where the crop rectangle > > sizes are actually applied to the ISC in this series ? Could you point > > me to it for sake of my better understanding ? > > The ISC is already doing that for some time now: > > https://elixir.bootlin.com/linux/latest/source/drivers/media/platform/atmel/atmel-isc-base.c#L459 > > Initially it was more like a safety feature, because I discovered that > there was no way of limiting the incoming pixels, and if some malevolent > entity would just keep sending pixels, it could lead to exploits. > > Now, the scaler is just exposing this to the user. > > > > >> With this being said, or having an understanding about the sink pad of > >> the ISC, how should I configure the sink frame size ? > > > > I don't think you should, the video device format is set through the > > usual S_FMT as it happens already. > > > > What I think it's missing in your implementation is only to propagate the > > crop rectangle size of the scaler sink pad to the scaler source pad > > format (as you don't have any composition rectangles yet) [1] > > > > With that done, you should be able to capture from your video device > > in the cropped dimensions. > > > > The (simplified) media pipeline will look something like > > > > sensor > > type: Subdev > > pad 0 (sink) (3280, 2462) > > -> scaler:0 > > > > scaler > > type: Subdev > > pad 0 (sink) (3280, 2464) > > crop: (3262, 2464) > > <- sensor:0 > > pad 1 (source) (3264, 2464) > > -> isc_common > > > > isc_common > > type V4L2 > > pad 0 (sink) > > <- scaler:1 > > > > And the video device format set to (3264, 2464) > > > > Then later you might want to implement s_selection on the scaler to have > > the crop size configurable. > > > > One additional question: can the crop be panned ? What I mean is that > > you explained you can instruct the ISC to drop pixels after a certain > > line lenght. This will cause the crop not to be centered on the input > > image. Can the ISC be configured to ignore data -before- and -after- > > a certain number of pixels or lines ? In that case you might want to > > handle the top,left corner of the crop rectangle too. Anyway, > > something for later.. > > Looking over this, it looks possible. I have not tried it. > It looks like configuring a minimum row /col number would make the ISC > drop the first n pixels/ first n lines. > > I will have to try this, but if it works, user could configure the > cropping area, that would be interesting ! Anyway I can't implement this > right now, but it's a nice thing to work on later. > > > > > [1] https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-subdev.html#order-of-configuration-and-format-propagation > > > >> Is there any possibility to avoid limits on the frame size and have it > >> arbitrary ? > >> I am 99% sure that v4l2-compliance will not be happy if there is no > >> limit on the frame on entity pads (hence, I bound-aligned the frame to > >> 16x16 up to 10k x 10k ) > >> > > > > That I'm not sure, but I think having size limits is required ? > > > > Anyway, from my side, the next version should be good to go. Thanks > > for sticking to all my comments and questions so far! > > What should I do about the 10000 x 10000 pixels ? > If only used for aligning in v4l_bound_align_image (&req_fmt->format.width, 16, 10000, 0, &req_fmt->format.height, 16, 10000, 0, 0); I think you can use UINT_MAX to make it more explicit ? > Thanks for reviewing ! > > Eugen > > > > Thanks > > j > > > > > >>> > >>>> > >>>>> > >>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + req_fmt->format.colorspace = V4L2_COLORSPACE_SRGB; > >>>>>>>>>> + req_fmt->format.field = V4L2_FIELD_NONE; > >>>>>>>>>> + req_fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > >>>>>>>>>> + req_fmt->format.quantization = V4L2_QUANTIZATION_DEFAULT; > >>>>>>>>>> + req_fmt->format.xfer_func = V4L2_XFER_FUNC_DEFAULT; > >>>>>>>>>> + > >>>>>>>>>> + fmt = isc_find_format_by_code(isc, req_fmt->format.code, &i); > >>>>>>>>> > >>>>>>>>> So you rely on the isc format list for the scaler as well ? > >>>>>>>>> I think it's fine as long as they are identical > >>>>>>>> > >>>>>>>> Yes, the scaler is kind of a dummy entity , but it's required by the > >>>>>>>> media controller validation of the pipeline. > >>>>>>>> > >>>>>>> > >>>>>>> More on this later > >>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + if (!fmt) > >>>>>>>>>> + fmt = &isc->formats_list[0]; > >>>>>>>>>> + > >>>>>>>>>> + req_fmt->format.code = fmt->mbus_code; > >>>>>>>>>> + > >>>>>>>>>> + if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) { > >>>>>>>>>> + v4l2_try_fmt = v4l2_subdev_get_try_format(sd, sd_state, > >>>>>>>>>> + req_fmt->pad); > >>>>>>>>>> + *v4l2_try_fmt = req_fmt->format; > >>>>>>>>>> + /* Trying on the pad sink makes the source sink change too */ > >>>>>>>>>> + if (req_fmt->pad == ISC_SCALER_PAD_SINK) { > >>>>>>>>>> + v4l2_try_fmt = > >>>>>>>>>> + v4l2_subdev_get_try_format(sd, sd_state, > >>>>>>>>>> + ISC_SCALER_PAD_SOURCE); > >>>>>>>>>> + *v4l2_try_fmt = req_fmt->format; > >>>>>>>>>> + > >>>>>>>>>> + v4l_bound_align_image(&v4l2_try_fmt->width, > >>>>>>>>>> + 16, isc->max_width, 0, > >>>>>>>>>> + &v4l2_try_fmt->height, > >>>>>>>>>> + 16, isc->max_height, 0, 0); > >>>>>>>>>> + } > >>>>>>>>>> + /* if we are just trying, we are done */ > >>>>>>>>>> + return 0; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + isc->scaler_format = req_fmt->format; > >>>>>>>>> > >>>>>>>>> No per-pad format but a global scaler_format ? How do you configure scaling ? > >>>>>>>>> > >>>>>>>>> Actually, I would like to know more about the scaler device > >>>>>>>>> capabilities. What functions can this IP perform ? Does it do > >>>>>>>>> cropping, can it also do (down)scaling or even composition ? > >>>>>>>>> > >>>>>>>>> I think it is worth to read > >>>>>>>>> https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-subdev.html#v4l2-subdev-selections > >>>>>>>>> where it is reported how cropping and scaling are implemented by using > >>>>>>>>> the selection API. > >>>>>>>>> > >>>>>>>>> Figure 4.5 is particularly helpful to explain the simple crop case, > >>>>>>>>> for which you need to implement support to by adding s_selection on > >>>>>>>>> the sink pad TGT_CROP target. > >>>>>>>> > >>>>>>>> The scaler is kind of a dummy entity. > >>>>>>>> The problem is that different subdevices cannot be connected directly to > >>>>>>>> a v4l entity, because the ISC does cropping on the incoming frame. > >>>>>>>> The ISC has a limit on the total number of pixels per frame, thus it > >>>>>>>> will do a crop. > >>>>>>> > >>>>>>> What is this limit related to ? I don't know the ISC internals, but is > >>>>>>> the limitation due to the overall image size, or maybe it's due to > >>>>>>> some line buffers being limited in the size they can transfer to > >>>>>>> memory ? Is it a limitation on the size of the image in pixels, or is > >>>>>>> it due to a limitation of the processing bandwidth on the input > >>>>>>> interface and thus depends on the actual size of the image in bytes ? > >>>>>> > >>>>>> Limitation is both on line size and full image size. From my > >>>>>> understanding, is that the internal SRAM is limited (8 Mpixels with a > >>>>>> small safe buffer on top in the case of sama7g5 ) > >>>>>> This SRAM could be used as 800000x10 for example, or 3200x2464 for > >>>>>> another example. However, I am not sure if a real line of 800,000 pixels > >>>>>> make sense, or if there is another internal mechanism in the ISC that > >>>>>> stops it. > >>>>>> In any case, the ISC is cropping the incoming frame from aaaXbbb up to > >>>>>> the maximum frame that it can handle > >>>>>> > >>>>>>> > >>>>>>>> So, if I allow e.g. an entity that sends 3280x2464 , when the ISC can > >>>>>>>> only handle 3264x2464 , then we have two choices: > >>>>>>>> 1/ crop it and output the 3264x2464 -> cannot, because userspace expects > >>>>>>>> 3280x2464, and the whole pipeline fails to configure, there is a > >>>>>>>> mismatch between /dev/video output and what the whole pipeline produces > >>>>>>> > >>>>>>> I'm confused in first place. If the ISC cannot output anything larger > >>>>>>> than 3264x2464, then if userspace sets a 3280x2464 size on the ISC, > >>>>>>> this should be adjusted to 3264x2464. > >>>>>> > >>>>>> yes, that is true. However, without an entity that says 'I am doing > >>>>>> cropping', the media pipeline will fail, because previous entity will > >>>>>> output 3280x2464 . > >>>>>> The conclusion to create a scaler entitity was done during my last year > >>>>>> discussions on the IRC. I could not find a way to perform this cropping > >>>>>> at the v4l entity side, and one reason is that the v4l entity does not > >>>>>> support what I needed , or at least that was my understanding. > >>>>>> You claim that the v4l entity could perform the cropping in the same > >>>>>> entity ? > >>>>> > >>>>> I thought so, but I haven't tried to be honest > >>>>> > >>>>>> > >>>>>> Let's assume for a minute that it could do this. Even so, I would prefer > >>>>>> to have a separate scaler entity to do this , for several reasons: > >>>>>> -> I would like to be able to crop the frame arbitrarily if I want to, > >>>>>> and the scaler would allow me to do this easier > >>>>>> -> it would be more obvious that the frame is cropped by having this > >>>>>> entity there > >>>>>> -> in fact some of the ISC versions really have a down scaler, that I > >>>>>> have not implemented yet. So it would be useful to already have this in > >>>>>> place to be able to scale down with it at a later time. > >>>>> > >>>>> I think this last reason is enough to have an entity plumbed in > >>>>> already > >>>>> > >>>>>> > >>>>>>> > >>>>>>> Or is the ISC limitation on the -input- side ? > >>>>>>> > >>>>>>>> 2/ deny the link, and ask the subdevice to produce the 3264x2464 which > >>>>>>>> the ISC can handle at most -> cannot , because the sensor let's say can > >>>>>>>> *only* produce 3280x2464 and *any* other frame size returns an error. > >>>>>>> > >>>>>>> buggy sensor I would say :) > >>>>>> > >>>>>> I cannot tell this to the customers, and I cannot fail the whole > >>>>>> pipeline because the sensor does not crop 16 pixels, when in fact I can > >>>>>> do this very easily on the ISC side. > >>>>>> > >>>>> > >>>>> Fair enough :) > >>>>> > >>>>>>> > >>>>>>>> > >>>>>>>> The only solution to make both worlds happy, is to have a dummy entity > >>>>>>>> called 'scaler' which in fact now it only performs a simple crop. > >>>>>>>> It accepts any frame size at input (hence the 10000x10000 which you saw > >>>>>>>> earlier), and outputs at most 3264x2464 the isc_max_height and > >>>>>>>> isc_max_width. > >>>>>>> > >>>>>>> I have a maybe dumb question: can the cropping operation be modeled > >>>>>>> by applying a TGT_CROP rectangle (whose _BOUND size is 3280x2464) to > >>>>>>> the atmel_isc_common entity sink pad and avoid the scaler completely ? > >>>>>> > >>>>>> I am not sure. This is what I wanted initially. But then I thought about > >>>>>> the three reasons stated above for the use of the scaler entity, and > >>>>>> discussed with folks on the IRC, and come up with the solution for the > >>>>>> scaler entity > >>>>>> > >>>>>>> > >>>>>>>> > >>>>>>>> So to answer your question, the isc scaler is a software model for a > >>>>>>>> simple cropping, that would make media controller happy, and capture > >>>>>>>> software happy. > >>>>>>>> > >>>>>>>> Here it how it looks : > >>>>>>>> > >>>>>>>> Device topology > >>>>>>>> - entity 1: atmel_isc_scaler (2 pads, 2 links) > >>>>>>>> type V4L2 subdev subtype Unknown flags 0 > >>>>>>>> device node name /dev/v4l-subdev0 > >>>>>>>> pad0: Sink > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb > >>>>>>>> crop.bounds:(0,0)/3264x2464 > >>>>>>>> crop:(0,0)/3264x2464] > >>>>>>>> <- "csi2dc":1 [ENABLED,IMMUTABLE] > >>>>>>>> pad1: Source > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb] > >>>>>>>> -> "atmel_isc_common":0 [ENABLED,IMMUTABLE] > >>>>>>>> > >>>>>>>> - entity 4: csi2dc (2 pads, 2 links) > >>>>>>>> type V4L2 subdev subtype Unknown flags 0 > >>>>>>>> device node name /dev/v4l-subdev1 > >>>>>>>> pad0: Sink > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb] > >>>>>>>> <- "dw-csi.0":1 [ENABLED] > >>>>>>>> pad1: Source > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb] > >>>>>>>> -> "atmel_isc_scaler":0 [ENABLED,IMMUTABLE] > >>>>>>>> > >>>>>>>> - entity 7: dw-csi.0 (2 pads, 2 links) > >>>>>>>> type V4L2 subdev subtype Unknown flags 0 > >>>>>>>> device node name /dev/v4l-subdev2 > >>>>>>>> pad0: Sink > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464] > >>>>>>>> <- "imx219 1-0010":0 [ENABLED] > >>>>>>>> pad1: Source > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464] > >>>>>>>> -> "csi2dc":0 [ENABLED] > >>>>>>>> > >>>>>>>> - entity 12: imx219 1-0010 (1 pad, 1 link) > >>>>>>>> type V4L2 subdev subtype Sensor flags 0 > >>>>>>>> device node name /dev/v4l-subdev3 > >>>>>>>> pad0: Source > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb > >>>>>>>> xfer:srgb ycbcr:601 quantization:full-range > >>>>>>>> crop.bounds:(8,8)/3280x2464 > >>>>>>>> crop:(8,8)/3280x2464] > >>>>>>>> -> "dw-csi.0":0 [ENABLED] > >>>>>>>> > >>>>>>>> - entity 24: atmel_isc_common (1 pad, 1 link) > >>>>>>>> type Node subtype V4L flags 1 > >>>>>>>> device node name /dev/video0 > >>>>>>>> pad0: Sink > >>>>>>>> <- "atmel_isc_scaler":1 [ENABLED,IMMUTABLE] > >>>>>>>> > >>>>>>>> > >>>>>>>> Scaler does this one cute little thing : > >>>>>>>> > >>>>>>>> pad0: Sink > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb > >>>>>>>> crop.bounds:(0,0)/3264x2464 > >>>>>>>> crop:(0,0)/3264x2464] > >>>>>>>> <- "csi2dc":1 [ENABLED,IMMUTABLE] > >>>>>>>> pad1: Source > >>>>>>>> [fmt:SRGGB10_1X10/3280x2464 field:none colorspace:srgb] > >>>>>>> > >>>>>>> Shouldn't this be 3264x2464 as that's what the entity outputs after > >>>>>>> the cropping ? And shouldn't then be 3264x2464 the size output from > >>>>>>> the video device too ? > >>>>>> > >>>>>> That's right. > >>>>>> I don't know why the source format for the scaler is still 3280x2464. > >>>>>> Maybe there is a bug on g_fmt for it.. have to check it. > >>>>> > >>>>> Thanks, I think this should be fixed ten > >>>>> > >>>>> > >>>>>> > >>>>>> Anyway, the video format is like this : > >>>>>> > >>>>>> # v4l2-ctl --get-fmt-video > >>>>>> Format Video Capture: > >>>>>> Width/Height : 3264/2464 > >>>>>> Pixel Format : 'RGBP' (16-bit RGB 5-6-5) > >>>>>> Field : None > >>>>>> Bytes per Line : 6528 > >>>>>> Size Image : 16084992 > >>>>>> Colorspace : sRGB > >>>>>> Transfer Function : Default (maps to sRGB) > >>>>>> YCbCr/HSV Encoding: Default (maps to ITU-R 601) > >>>>>> Quantization : Default (maps to Full Range) > >>>>>> Flags : > >>>>>> # > >>>>>> > >>>>>> and initializing the pipeline looks fine from user perspective. > >>>>>> The scaler is in fact the solution to make this pipeline work with > >>>>>> libcamera. > >>>>>> I found this cropping to be an issue in media controller when trying it > >>>>>> with libcamera. Otherwise, the other user space apps which I was using > >>>>>> never complained that anything was wrong > >>>>>> Libcamera simply refuses to acknowledge the pipeline if the video output > >>>>>> is 3264x2464 but there is no entity that changes the format from > >>>>>> 3280x2464 down > >>>>> > >>>>> Not sure why libcamera should play a role there. Isn't it the media > >>>>> pipeline validation that complains and returns an -EPIPE ? > >>>> > >>>> I tried this before the media_pipeline_start . > >>>> Perhaps libcamera was doing some similar checks, anyway, it was > >>>> complaining and not adding the stream, and reported no usable streams/camera > >>>> > >>>> Once I will rework some of the things according to your review I will > >>>> run libcamera again to see what it complains about > >>>> > >>>>> > >>>>>> > >>>>>>> > >>>>>>> > >>>>>>>> -> "atmel_isc_common":0 [ENABLED,IMMUTABLE] > >>>>>>>> > >>>>>>>> > >>>>>>>> Which is what we needed. > >>>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + return 0; > >>>>>>>>>> +} > >>>>>>>>>> + > >>>>>>>>>> +static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd, > >>>>>>>>>> + struct v4l2_subdev_state *sd_state, > >>>>>>>>>> + struct v4l2_subdev_mbus_code_enum *code) > >>>>>>>>>> +{ > >>>>>>>>>> + struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); > >>>>>>>>>> + int supported_index = 0; > >>>>>>>>>> + int i; > >>>>>>>>>> + > >>>>>>>>>> + for (i = 0; i < isc->formats_list_size; i++) { > >>>>>>>>>> + if (!isc->formats_list[i].sd_support) > >>>>>>>>>> + continue; > >>>>>>>>> > >>>>>>>>> The sd_support flag still doesn't click in my head. > >>>>>>>>> > >>>>>>>>> Shouldn't the enumeration of available formats on the scaler do not > >>>>>>>>> depend on the sensor supproted formats ? > >>>>>>>> > >>>>>>>> You're right. I will have to check it again. > >>>>>>>> > >>>>>>>>> > >>>>>>>>>> + if (supported_index == code->index) { > >>>>>>>>>> + code->code = isc->formats_list[i].mbus_code; > >>>>>>>>>> + return 0; > >>>>>>>>>> + } > >>>>>>>>>> + supported_index++; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + return -EINVAL; > >>>>>>>>>> +} > >>>>>>>>>> + > >>>>>>>>>> +static int isc_scaler_g_sel(struct v4l2_subdev *sd, > >>>>>>>>>> + struct v4l2_subdev_state *sd_state, > >>>>>>>>>> + struct v4l2_subdev_selection *sel) > >>>>>>>>>> +{ > >>>>>>>>>> + struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); > >>>>>>>>>> + > >>>>>>>>>> + if (sel->pad == ISC_SCALER_PAD_SOURCE) > >>>>>>>>>> + return -EINVAL; > >>>>>>>>>> + > >>>>>>>>>> + if (sel->target != V4L2_SEL_TGT_CROP_BOUNDS && > >>>>>>>>>> + sel->target != V4L2_SEL_TGT_CROP) > >>>>>>>>>> + return -EINVAL; > >>>>>>>>>> + > >>>>>>>>>> + sel->r.height = isc->max_height; > >>>>>>>>>> + sel->r.width = isc->max_width; > >>>>>>>>> > >>>>>>>>> The CROP_BOUNDS should be set to the same size as the sink pad image format, > >>>>>>>>> as it represents the maximum valid crop rectangle. > >>>>>>>>> > >>>>>>>>> TGT_CROP should report the configured crop rectangle which can be > >>>>>>>>> intiialized to the same size as CROP_BOUNDS, if my understanding of > >>>>>>>>> the spec is correct > >>>>>>>> > >>>>>>>> So you would like to have this differentiated, and report the > >>>>>>>> CROP_BOUNDS to whatever is on the sink pad, and the TGT_CROP to what is > >>>>>>>> reported now, the maximum size of the ISC frame . > >>>>>>>> My understanding is correct ? > >>>>>>>> > >>>>>>> > >>>>>>> I didn't know you have an HW limitation, so your _BOUNDS is not the > >>>>>>> input image size but rather 3264x2464 ( == max_width x max_height). > >>>>>>> > >>>>>>> What I meant is that _BOUNDS should report the maximum rectangle size > >>>>>>> that can be applied to the _CROP target. In you case you have an HW > >>>>>>> limitation 3264x2464 and that's the largest rectangle you can apply. > >>>>>> So the CROP should be at 3264x2464 > >>>>>>> TGT_CROP can be initialized to the same as _BOUND, but if you > >>>>>>> implement s_selection it should report what has been there applied. > >>>>>> and BOUND to actual frame size ? > >>>>>>> But as you don't implement s_selection yet, I think this is fine for > >>>>>>> now. Maybe a little comment ? > >>>>>> > >>>>>> It could also be the case where e.g. the sensor is outputting 1920x1080, > >>>>>> in this case the scaler would do nothing. > >>>>>> CROP is still 3264x2464 and BOUND in this case is 1920x1080 ? > >>>>>> If the sensor is outputting 1920x1080, this format comes directly from > >>>>>> the sensor (the sensor is cropping it from 3280x2464 or not... it's the > >>>>>> sensor's problem) > >>>>>>> > >>>>>>> Also, is set->r zeroed by the framework before getting here ? > >>>>>>> Otherwise you should set r.left and r.top to 0 > >>>>>> > >>>>>> If these are redundant, no problem to remove them > >>>>> > >>>>> I was actually confused. I was suggesting you to add... > >>>>> > >>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + sel->r.left = 0; > >>>>>>>>>> + sel->r.top = 0; > >>>>> > >>>>> These ^ > >>>>> > >>>>> So nothing to change. Sorry I've missed them > >>>>> > >>>>> Thanks > >>>>> j > >>>>> > >>>>> > >>>>>>>>>> + > >>>>>>>>>> + return 0; > >>>>>>>>>> +} > >>>>>>>>>> + > >>>>>>>>>> +static int isc_scaler_init_cfg(struct v4l2_subdev *sd, > >>>>>>>>>> + struct v4l2_subdev_state *sd_state) > >>>>>>>>>> +{ > >>>>>>>>>> + struct v4l2_mbus_framefmt *v4l2_try_fmt = > >>>>>>>>>> + v4l2_subdev_get_try_format(sd, sd_state, 0); > >>>>>>>>>> + struct isc_device *isc = container_of(sd, struct isc_device, scaler_sd); > >>>>>>>>>> + > >>>>>>>>>> + *v4l2_try_fmt = isc->scaler_format; > >>>>>>>>>> + > >>>>>>>>>> + return 0; > >>>>>>>>>> +} > >>>>>>>>>> + > >>>>>>>>>> +static const struct v4l2_subdev_pad_ops isc_scaler_pad_ops = { > >>>>>>>>>> + .enum_mbus_code = isc_scaler_enum_mbus_code, > >>>>>>>>>> + .set_fmt = isc_scaler_set_fmt, > >>>>>>>>>> + .get_fmt = isc_scaler_get_fmt, > >>>>>>>>>> + .get_selection = isc_scaler_g_sel, > >>>>>>>>>> + .init_cfg = isc_scaler_init_cfg, > >>>>>>>>> > >>>>>>>>> .link_validate = v4l2_subdev_link_validate_default, > >>>>>>>>> > >>>>>>>>> To have the formats at the end of links that point to this entity > >>>>>>>>> validated (I think the framework already calls it if not set though, > >>>>>>>>> please check v4l2-subdev.c:v4l2_subdev_link_validate()) > >>>>>>>>> > >>>>>>>>>> +}; > >>>>>>>>>> + > >>>>>>>>>> +static const struct v4l2_subdev_ops xisc_scaler_subdev_ops = { > >>>>>>>>>> + .pad = &isc_scaler_pad_ops, > >>>>>>>>>> +}; > >>>>>>>>>> + > >>>>>>>>>> +int isc_scaler_init(struct isc_device *isc) > >>>>>>>>>> +{ > >>>>>>>>>> + int ret; > >>>>>>>>>> + > >>>>>>>>>> + v4l2_subdev_init(&isc->scaler_sd, &xisc_scaler_subdev_ops); > >>>>>>>>>> + > >>>>>>>>>> + isc->scaler_sd.owner = THIS_MODULE; > >>>>>>>>>> + isc->scaler_sd.dev = isc->dev; > >>>>>>>>>> + snprintf(isc->scaler_sd.name, sizeof(isc->scaler_sd.name), > >>>>>>>>>> + "atmel_isc_scaler"); > >>>>>>>>> > >>>>>>>>> I would drop 'atmel' for brevity, unless other entities have this > >>>>>>>>> prefix set already > >>>>>>>> > >>>>>>>> The v4l entity takes it's name from the module name, which is > >>>>>>>> atmel_isc_common, so I thought to keep the prefix > >>>>>>>> > >>>>>>> > >>>>>>> Ack! > >>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + isc->scaler_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; > >>>>>>>>>> + isc->scaler_sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; > >>>>>>>>>> + isc->scaler_pads[ISC_SCALER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; > >>>>>>>>>> + isc->scaler_pads[ISC_SCALER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; > >>>>>>>>>> + > >>>>>>>>>> + isc->scaler_format.height = isc->max_height; > >>>>>>>>>> + isc->scaler_format.width = isc->max_width; > >>>>>>>>>> + isc->scaler_format.code = isc->formats_list[0].mbus_code; > >>>>>>>>>> + isc->scaler_format.colorspace = V4L2_COLORSPACE_SRGB; > >>>>>>>>>> + isc->scaler_format.field = V4L2_FIELD_NONE; > >>>>>>>>>> + isc->scaler_format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; > >>>>>>>>>> + isc->scaler_format.quantization = V4L2_QUANTIZATION_DEFAULT; > >>>>>>>>>> + isc->scaler_format.xfer_func = V4L2_XFER_FUNC_DEFAULT; > >>>>>>>>>> + > >>>>>>>>>> + ret = media_entity_pads_init(&isc->scaler_sd.entity, > >>>>>>>>>> + ISC_SCALER_PADS_NUM, > >>>>>>>>>> + isc->scaler_pads); > >>>>>>>>>> + if (ret < 0) { > >>>>>>>>>> + dev_err(isc->dev, "scaler sd media entity init failed\n"); > >>>>>>>>>> + return ret; > >>>>>>>>>> + } > >>>>>>>>>> + ret = v4l2_device_register_subdev(&isc->v4l2_dev, &isc->scaler_sd); > >>>>>>>>>> + if (ret < 0) { > >>>>>>>>>> + dev_err(isc->dev, "scaler sd failed to register subdev\n"); > >>>>>>>>>> + return ret; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + return ret; > >>>>>>>>>> +} > >>>>>>>>>> +EXPORT_SYMBOL_GPL(isc_scaler_init); > >>>>>>>>>> + > >>>>>>>>>> +int isc_scaler_link(struct isc_device *isc) > >>>>>>>>>> +{ > >>>>>>>>>> + int ret; > >>>>>>>>>> + > >>>>>>>>>> + ret = media_create_pad_link(&isc->current_subdev->sd->entity, > >>>>>>>>>> + isc->remote_pad, &isc->scaler_sd.entity, > >>>>>>>>>> + ISC_SCALER_PAD_SINK, > >>>>>>>>>> + MEDIA_LNK_FL_ENABLED | > >>>>>>>>>> + MEDIA_LNK_FL_IMMUTABLE); > >>>>>>>>>> + > >>>>>>>>>> + if (ret < 0) { > >>>>>>>>>> + v4l2_err(&isc->v4l2_dev, > >>>>>>>>>> + "Failed to create pad link: %s to %s\n", > >>>>>>>>>> + isc->current_subdev->sd->entity.name, > >>>>>>>>>> + isc->scaler_sd.entity.name); > >>>>>>>>>> + return ret; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + dev_dbg(isc->dev, "link with %s pad: %d\n", > >>>>>>>>>> + isc->current_subdev->sd->name, isc->remote_pad); > >>>>>>>>>> + > >>>>>>>>>> + ret = media_create_pad_link(&isc->scaler_sd.entity, > >>>>>>>>>> + ISC_SCALER_PAD_SOURCE, > >>>>>>>>>> + &isc->video_dev.entity, ISC_PAD_SINK, > >>>>>>>>>> + MEDIA_LNK_FL_ENABLED | > >>>>>>>>>> + MEDIA_LNK_FL_IMMUTABLE); > >>>>>>>>>> + > >>>>>>>>>> + if (ret < 0) { > >>>>>>>>>> + v4l2_err(&isc->v4l2_dev, > >>>>>>>>>> + "Failed to create pad link: %s to %s\n", > >>>>>>>>>> + isc->scaler_sd.entity.name, > >>>>>>>>>> + isc->video_dev.entity.name); > >>>>>>>>>> + return ret; > >>>>>>>>>> + } > >>>>>>>>>> + > >>>>>>>>>> + dev_dbg(isc->dev, "link with %s pad: %d\n", isc->scaler_sd.name, > >>>>>>>>>> + ISC_SCALER_PAD_SOURCE); > >>>>>>>>>> + > >>>>>>>>>> + return ret; > >>>>>>>>>> +} > >>>>>>>>>> +EXPORT_SYMBOL_GPL(isc_scaler_link); > >>>>>>>>> > >>>>>>>>> From the DT graph point of view, the ISC appears as a single block > >>>>>>>>> with an CSI-2 input port. It is then in charge of parsing the .dts and > >>>>>>>>> add to its notifier the image sensor remote subdevice. > >>>>>>>> > >>>>>>>> Actually it's only a parallel port. > >>>>>>>> And it can be connected either to a sensor directly or to the csi2dc > >>>>>>>> bridge. (the csi2dc bridge can be connected to a CSI-2 receiver) > >>>>>>>> > >>>>>>>>> > >>>>>>>>> When the sensor subdev registers and the ISC notifier completes, the scaler > >>>>>>>>> entity which was initialized at isc_probe() time is linked in between > >>>>>>>>> the ISC and the image sensor immutably. > >>>>>>>> > >>>>>>>> yes, because this cannot change at runtime, and usually, it can't change > >>>>>>>> without altering the board hardware. (at least this is what my > >>>>>>>> understanding of immutability is ) > >>>>>>>> > >>>>>>>>> > >>>>>>>>> I think it is fine for now, but I wonder if you plan to plumb more > >>>>>>>>> components between the ISC video node and the sensor, if it's not > >>>>>>>>> worth changing the DT bindings and their parsing logic to separate the > >>>>>>>>> CSI-2 receiver from the ISC, whcih can create its media graph at probe > >>>>>>>>> time and have the CSI-2 receiver entity as downstream remote. I think > >>>>>>>>> I need to get to know the ISC better to really have an idea. For now, > >>>>>>>>> this seems ok to me, but please check with maintainers if this is fine > >>>>>>>>> with them. > >>>>>>>> > >>>>>>>> In the XISC case (sama7g5-isc), the csi2dc receiver is the subdev (so, > >>>>>>>> the remote subdev). > >>>>>>>> The ISC will register a scaler, and connect the subdev to this scaler > >>>>>>>> first, and then, the scaler to the isc itself (the v4l entity). > >>>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h > >>>>>>>>>> index 5fbf52a9080b..c9234c90ae58 100644 > >>>>>>>>>> --- a/drivers/media/platform/atmel/atmel-isc.h > >>>>>>>>>> +++ b/drivers/media/platform/atmel/atmel-isc.h > >>>>>>>>>> @@ -183,6 +183,17 @@ struct isc_reg_offsets { > >>>>>>>>>> u32 his_entry; > >>>>>>>>>> }; > >>>>>>>>>> > >>>>>>>>>> +enum isc_mc_pads { > >>>>>>>>>> + ISC_PAD_SINK = 0, > >>>>>>>>>> + ISC_PADS_NUM = 1, > >>>>>>>>>> +}; > >>>>>>>>>> + > >>>>>>>>>> +enum isc_scaler_pads { > >>>>>>>>>> + ISC_SCALER_PAD_SINK = 0, > >>>>>>>>>> + ISC_SCALER_PAD_SOURCE = 1, > >>>>>>>>>> + ISC_SCALER_PADS_NUM = 2, > >>>>>>>>>> +}; > >>>>>>>>>> + > >>>>>>>>>> /* > >>>>>>>>>> * struct isc_device - ISC device driver data/config struct > >>>>>>>>>> * @regmap: Register map > >>>>>>>>>> @@ -257,6 +268,12 @@ struct isc_reg_offsets { > >>>>>>>>>> * be used as an input to the controller > >>>>>>>>>> * @controller_formats_size: size of controller_formats array > >>>>>>>>>> * @formats_list_size: size of formats_list array > >>>>>>>>>> + * @pads: media controller pads for isc video entity > >>>>>>>>>> + * @mdev: media device that is registered by the isc > >>>>>>>>>> + * @remote_pad: remote pad on the connected subdevice > >>>>>>>>>> + * @scaler_sd: subdevice for the scaler that isc registers > >>>>>>>>>> + * @scaler_pads: media controller pads for the scaler subdevice > >>>>>>>>>> + * @scaler_format: current format for the scaler subdevice > >>>>>>>>>> */ > >>>>>>>>>> struct isc_device { > >>>>>>>>>> struct regmap *regmap; > >>>>>>>>>> @@ -344,6 +361,19 @@ struct isc_device { > >>>>>>>>>> struct isc_format *formats_list; > >>>>>>>>>> u32 controller_formats_size; > >>>>>>>>>> u32 formats_list_size; > >>>>>>>>>> + > >>>>>>>>>> + struct { > >>>>>>>>>> + struct media_pad pads[ISC_PADS_NUM]; > >>>>>>>>>> + struct media_device mdev; > >>>>>>>>>> + > >>>>>>>>>> + u32 remote_pad; > >>>>>>>>>> + }; > >>>>>>>>>> + > >>>>>>>>>> + struct { > >>>>>>>>>> + struct v4l2_subdev scaler_sd; > >>>>>>>>>> + struct media_pad scaler_pads[ISC_SCALER_PADS_NUM]; > >>>>>>>>>> + struct v4l2_mbus_framefmt scaler_format; > >>>>>>>> > >>>>>>>> Here are the scaler stuff which I added, in the same bulk struct for the > >>>>>>>> whole isc device > >>>>>>>> > >>>>>>>> > >>>>>>>>>> + }; > >>>>>>>>>> }; > >>>>>>>>>> > >>>>>>>>>> extern const struct regmap_config isc_regmap_config; > >>>>>>>>>> @@ -355,4 +385,11 @@ int isc_clk_init(struct isc_device *isc); > >>>>>>>>>> void isc_subdev_cleanup(struct isc_device *isc); > >>>>>>>>>> void isc_clk_cleanup(struct isc_device *isc); > >>>>>>>>>> > >>>>>>>>>> +int isc_scaler_link(struct isc_device *isc); > >>>>>>>>>> +int isc_scaler_init(struct isc_device *isc); > >>>>>>>>>> +int isc_mc_init(struct isc_device *isc, u32 ver); > >>>>>>>>>> +void isc_mc_cleanup(struct isc_device *isc); > >>>>>>>>>> + > >>>>>>>>>> +struct isc_format *isc_find_format_by_code(struct isc_device *isc, > >>>>>>>>>> + unsigned int code, int *index); > >>>>>>>>>> #endif > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c > >>>>>>>>>> index c5b9563e36cb..c244682ea22f 100644 > >>>>>>>>>> --- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c > >>>>>>>>>> +++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c > >>>>>>>>>> @@ -553,6 +553,12 @@ static int atmel_isc_probe(struct platform_device *pdev) > >>>>>>>>>> break; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> + regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); > >>>>>>>>> > >>>>>>>>> I am surprised you can read a register before runtime_pm is > >>>>>>>>> intialized! > >>>>>>>>> > >>>>>>>> > >>>>>>>> Actually I can read any register after the moment of starting the clock > >>>>>>>> on the hardware block. > >>>>>>> > >>>>>>> Ah right then > >>>>>>> > >>>>>>>> Maybe the starting and stopping of the clock needs to be moved to the > >>>>>>>> runtime_pm calls, but this is another story, not related to the media > >>>>>>>> controller. > >>>>>>> > >>>>>>> It's fine, but maybe worth recording with a todo ? > >>>>>>> > >>>>>>> Thanks > >>>>>>> j > >>>>>>> > >>>>>>>> I moved this line because I had to pass the version to the isc_mc_init call > >>>>>>>> > >>>>>>>>> Thanks > >>>>>>>>> j > >>>>>>>> > >>>>>>>> Thanks for reviewing ! > >>>>>>>> Eugen > >>>>>>>>> > >>>>>>>>> > >>>>>>>>>> + > >>>>>>>>>> + ret = isc_mc_init(isc, ver); > >>>>>>>>>> + if (ret < 0) > >>>>>>>>>> + goto isc_probe_mc_init_err; > >>>>>>>>>> + > >>>>>>>>>> pm_runtime_set_active(dev); > >>>>>>>>>> pm_runtime_enable(dev); > >>>>>>>>>> pm_request_idle(dev); > >>>>>>>>>> @@ -562,7 +568,7 @@ static int atmel_isc_probe(struct platform_device *pdev) > >>>>>>>>>> ret = clk_prepare_enable(isc->ispck); > >>>>>>>>>> if (ret) { > >>>>>>>>>> dev_err(dev, "failed to enable ispck: %d\n", ret); > >>>>>>>>>> - goto cleanup_subdev; > >>>>>>>>>> + goto isc_probe_mc_init_err; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> /* ispck should be greater or equal to hclock */ > >>>>>>>>>> @@ -572,7 +578,6 @@ static int atmel_isc_probe(struct platform_device *pdev) > >>>>>>>>>> goto unprepare_clk; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); > >>>>>>>>>> dev_info(dev, "Microchip ISC version %x\n", ver); > >>>>>>>>>> > >>>>>>>>>> return 0; > >>>>>>>>>> @@ -580,6 +585,9 @@ static int atmel_isc_probe(struct platform_device *pdev) > >>>>>>>>>> unprepare_clk: > >>>>>>>>>> clk_disable_unprepare(isc->ispck); > >>>>>>>>>> > >>>>>>>>>> +isc_probe_mc_init_err: > >>>>>>>>>> + isc_mc_cleanup(isc); > >>>>>>>>>> + > >>>>>>>>>> cleanup_subdev: > >>>>>>>>>> isc_subdev_cleanup(isc); > >>>>>>>>>> > >>>>>>>>>> @@ -600,6 +608,8 @@ static int atmel_isc_remove(struct platform_device *pdev) > >>>>>>>>>> > >>>>>>>>>> pm_runtime_disable(&pdev->dev); > >>>>>>>>>> > >>>>>>>>>> + isc_mc_cleanup(isc); > >>>>>>>>>> + > >>>>>>>>>> isc_subdev_cleanup(isc); > >>>>>>>>>> > >>>>>>>>>> v4l2_device_unregister(&isc->v4l2_dev); > >>>>>>>>>> diff --git a/drivers/media/platform/atmel/atmel-sama7g5-isc.c b/drivers/media/platform/atmel/atmel-sama7g5-isc.c > >>>>>>>>>> index 07a80b08bc54..9dc75eed0098 100644 > >>>>>>>>>> --- a/drivers/media/platform/atmel/atmel-sama7g5-isc.c > >>>>>>>>>> +++ b/drivers/media/platform/atmel/atmel-sama7g5-isc.c > >>>>>>>>>> @@ -547,15 +547,23 @@ static int microchip_xisc_probe(struct platform_device *pdev) > >>>>>>>>>> break; > >>>>>>>>>> } > >>>>>>>>>> > >>>>>>>>>> + regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); > >>>>>>>>>> + > >>>>>>>>>> + ret = isc_mc_init(isc, ver); > >>>>>>>>>> + if (ret < 0) > >>>>>>>>>> + goto isc_probe_mc_init_err; > >>>>>>>>>> + > >>>>>>>>>> pm_runtime_set_active(dev); > >>>>>>>>>> pm_runtime_enable(dev); > >>>>>>>>>> pm_request_idle(dev); > >>>>>>>>>> > >>>>>>>>>> - regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver); > >>>>>>>>>> dev_info(dev, "Microchip XISC version %x\n", ver); > >>>>>>>>>> > >>>>>>>>>> return 0; > >>>>>>>>>> > >>>>>>>>>> +isc_probe_mc_init_err: > >>>>>>>>>> + isc_mc_cleanup(isc); > >>>>>>>>>> + > >>>>>>>>>> cleanup_subdev: > >>>>>>>>>> isc_subdev_cleanup(isc); > >>>>>>>>>> > >>>>>>>>>> @@ -576,6 +584,8 @@ static int microchip_xisc_remove(struct platform_device *pdev) > >>>>>>>>>> > >>>>>>>>>> pm_runtime_disable(&pdev->dev); > >>>>>>>>>> > >>>>>>>>>> + isc_mc_cleanup(isc); > >>>>>>>>>> + > >>>>>>>>>> isc_subdev_cleanup(isc); > >>>>>>>>>> > >>>>>>>>>> v4l2_device_unregister(&isc->v4l2_dev); > >>>>>>>>>> -- > >>>>>>>>>> 2.25.1 > >>>>>>>>>> > >>>>>>>> > >>>>>> > >>>> > >> >