<div dir="ltr"><div dir="ltr">Hi David,<div><br></div><div>Thank you for the feedback.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 22 Oct 2021 at 16:16, David Plowman <<a href="mailto:david.plowman@raspberrypi.com">david.plowman@raspberrypi.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Naush<br>
<br>
Thanks for the new version. I think a lot of stuff is tidier now that<br>
we're clear that we're selecting a subdev format.<br>
<br>
On Fri, 22 Oct 2021 at 15:40, Naushir Patuck <<a href="mailto:naush@raspberrypi.com" target="_blank">naush@raspberrypi.com</a>> wrote:<br>
><br>
> Switch the pipeline handler to use the new Unicam media controller based driver.<br>
> With this change, we directly talk to the sensor device driver to set controls<br>
> and set/get formats in the pipeline handler.<br>
><br>
> This change requires the accompanying Raspberry Pi linux kernel change at<br>
> <a href="https://github.com/raspberrypi/linux/pull/4645" rel="noreferrer" target="_blank">https://github.com/raspberrypi/linux/pull/4645</a>. If this kernel change is not<br>
> present, the pipeline handler will fail to run with an error message informing<br>
> the user to update the kernel build.<br>
><br>
> Signed-off-by: Naushir Patuck <<a href="mailto:naush@raspberrypi.com" target="_blank">naush@raspberrypi.com</a>><br>
> ---<br>
>  .../pipeline/raspberrypi/raspberrypi.cpp      | 146 +++++++++++-------<br>
>  1 file changed, 90 insertions(+), 56 deletions(-)<br>
><br>
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> index 1634ca98f481..a31b0f81eba7 100644<br>
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp<br>
> @@ -48,6 +48,29 @@ LOG_DEFINE_CATEGORY(RPI)<br>
><br>
>  namespace {<br>
><br>
> +/* Map of mbus codes to supported sizes reported by the sensor. */<br>
> +using SensorFormats = std::map<unsigned int, std::vector<Size>>;<br>
> +<br>
> +SensorFormats populateSensorFormats(std::unique_ptr<CameraSensor> &sensor)<br>
> +{<br>
> +       SensorFormats formats;<br>
> +<br>
> +       for (auto const mbusCode : sensor->mbusCodes())<br>
> +               formats.emplace(mbusCode, sensor->sizes(mbusCode));<br>
> +<br>
> +       return formats;<br>
> +}<br>
> +<br>
> +inline V4L2DeviceFormat toV4L2DeviceFormat(V4L2SubdeviceFormat &mode)<br>
> +{<br>
> +       V4L2DeviceFormat deviceFormat;<br>
> +       BayerFormat bayer = BayerFormat::fromMbusCode(mode.mbus_code);<br>
> +<br>
> +       deviceFormat.fourcc = bayer.toV4L2PixelFormat();<br>
> +       deviceFormat.size = mode.size;<br>
> +       return deviceFormat;<br>
> +}<br>
> +<br>
<br>
I guess I wonder slightly whether this should be generally available,<br>
but it could easily be a change for another time. (Actually, same<br>
question for isRaw just below...)<br></blockquote><div><br></div><div>Agree, this can be addressed separately.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">>  bool isRaw(PixelFormat &pixFmt)<br>
>  {<br>
>         /*<br>
> @@ -74,11 +97,10 @@ double scoreFormat(double desired, double actual)<br>
>         return score;<br>
>  }<br>
><br>
> -V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap,<br>
> -                             const Size &req)<br>
> +V4L2SubdeviceFormat findBestMode(const SensorFormats &formatsMap, const Size &req)<br>
>  {<br>
>         double bestScore = std::numeric_limits<double>::max(), score;<br>
> -       V4L2DeviceFormat bestMode;<br>
> +       V4L2SubdeviceFormat bestMode;<br>
><br>
>  #define PENALTY_AR             1500.0<br>
>  #define PENALTY_8BIT           2000.0<br>
> @@ -88,18 +110,17 @@ V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap,<br>
><br>
>         /* Calculate the closest/best mode from the user requested size. */<br>
>         for (const auto &iter : formatsMap) {<br>
> -               V4L2PixelFormat v4l2Format = iter.first;<br>
> -               const PixelFormatInfo &info = PixelFormatInfo::info(v4l2Format);<br>
> +               const unsigned int mbus_code = iter.first;<br>
> +               const PixelFormat format = BayerFormat::fromMbusCode(mbus_code).toPixelFormat();<br>
> +               const PixelFormatInfo &info = PixelFormatInfo::info(format);<br>
><br>
> -               for (const SizeRange &sz : iter.second) {<br>
> -                       double modeWidth = sz.contains(req) ? req.width : sz.max.width;<br>
> -                       double modeHeight = sz.contains(req) ? req.height : sz.max.height;<br>
> +               for (const Size &sz : iter.second) {<br>
>                         double reqAr = static_cast<double>(req.width) / req.height;<br>
> -                       double modeAr = modeWidth / modeHeight;<br>
> +                       double modeAr = sz.width / sz.height;<br>
><br>
>                         /* Score the dimensions for closeness. */<br>
> -                       score = scoreFormat(req.width, modeWidth);<br>
> -                       score += scoreFormat(req.height, modeHeight);<br>
> +                       score = scoreFormat(req.width, sz.width);<br>
> +                       score += scoreFormat(req.height, sz.height);<br>
>                         score += PENALTY_AR * scoreFormat(reqAr, modeAr);<br>
><br>
>                         /* Add any penalties... this is not an exact science! */<br>
> @@ -115,12 +136,12 @@ V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap,<br>
><br>
>                         if (score <= bestScore) {<br>
>                                 bestScore = score;<br>
> -                               bestMode.fourcc = v4l2Format;<br>
> -                               bestMode.size = Size(modeWidth, modeHeight);<br>
> +                               bestMode.mbus_code = mbus_code;<br>
> +                               bestMode.size = sz;<br>
>                         }<br>
><br>
> -                       LOG(RPI, Info) << "Mode: " << modeWidth << "x" << modeHeight<br>
> -                                      << " fmt " << v4l2Format.toString()<br>
> +                       LOG(RPI, Info) << "Mode: " << sz.width << "x" << sz.height<br>
> +                                      << " fmt " << format.toString()<br>
>                                        << " Score: " << score<br>
>                                        << " (best " << bestScore << ")";<br>
>                 }<br>
> @@ -170,6 +191,7 @@ public:<br>
>         std::unique_ptr<ipa::RPi::IPAProxyRPi> ipa_;<br>
><br>
>         std::unique_ptr<CameraSensor> sensor_;<br>
> +       SensorFormats sensorFormats_;<br>
>         /* Array of Unicam and ISP device streams and associated buffers/streams. */<br>
>         RPi::Device<Unicam, 2> unicam_;<br>
>         RPi::Device<Isp, 4> isp_;<br>
> @@ -352,9 +374,9 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()<br>
>                          * Calculate the best sensor mode we can use based on<br>
>                          * the user request.<br>
>                          */<br>
> -                       V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats();<br>
> -                       V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size);<br>
> -                       int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat);<br>
> +                       V4L2SubdeviceFormat sensorFormat = findBestMode(data_->sensorFormats_, cfg.size);<br>
> +                       V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(sensorFormat);<br>
> +                       int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&unicamFormat);<br>
>                         if (ret)<br>
>                                 return Invalid;<br>
><br>
> @@ -366,7 +388,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()<br>
>                          * fetch the "native" (i.e. untransformed) Bayer order,<br>
>                          * because the sensor may currently be flipped!<br>
>                          */<br>
> -                       V4L2PixelFormat fourcc = sensorFormat.fourcc;<br>
> +                       V4L2PixelFormat fourcc = unicamFormat.fourcc;<br>
>                         if (data_->flipsAlterBayerOrder_) {<br>
>                                 BayerFormat bayer = BayerFormat::fromV4L2PixelFormat(fourcc);<br>
>                                 bayer.order = data_->nativeBayerOrder_;<br>
> @@ -375,15 +397,15 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()<br>
>                         }<br>
><br>
>                         PixelFormat sensorPixFormat = fourcc.toPixelFormat();<br>
> -                       if (cfg.size != sensorFormat.size ||<br>
> +                       if (cfg.size != unicamFormat.size ||<br>
>                             cfg.pixelFormat != sensorPixFormat) {<br>
> -                               cfg.size = sensorFormat.size;<br>
> +                               cfg.size = unicamFormat.size;<br>
>                                 cfg.pixelFormat = sensorPixFormat;<br>
>                                 status = Adjusted;<br>
>                         }<br>
><br>
> -                       cfg.stride = sensorFormat.planes[0].bpl;<br>
> -                       cfg.frameSize = sensorFormat.planes[0].size;<br>
> +                       cfg.stride = unicamFormat.planes[0].bpl;<br>
> +                       cfg.frameSize = unicamFormat.planes[0].size;<br>
><br>
>                         rawCount++;<br>
>                 } else {<br>
> @@ -472,7 +494,8 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera,<br>
>  {<br>
>         RPiCameraData *data = cameraData(camera);<br>
>         CameraConfiguration *config = new RPiCameraConfiguration(data);<br>
> -       V4L2DeviceFormat sensorFormat;<br>
> +       V4L2SubdeviceFormat sensorFormat;<br>
> +       V4L2DeviceFormat unicamFormat;<br>
>         unsigned int bufferCount;<br>
>         PixelFormat pixelFormat;<br>
>         V4L2VideoDevice::Formats fmts;<br>
> @@ -487,9 +510,9 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera,<br>
>                 switch (role) {<br>
>                 case StreamRole::Raw:<br>
>                         size = data->sensor_->resolution();<br>
> -                       fmts = data->unicam_[Unicam::Image].dev()->formats();<br>
> -                       sensorFormat = findBestMode(fmts, size);<br>
> -                       pixelFormat = sensorFormat.fourcc.toPixelFormat();<br>
> +                       sensorFormat = findBestMode(data->sensorFormats_, size);<br>
> +                       unicamFormat = toV4L2DeviceFormat(sensorFormat);<br>
> +                       pixelFormat = BayerFormat::fromMbusCode(sensorFormat.mbus_code).toPixelFormat();<br>
>                         ASSERT(pixelFormat.isValid());<br>
>                         bufferCount = 2;<br>
>                         rawCount++;<br>
> @@ -599,32 +622,30 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)<br>
>         }<br>
><br>
>         /* First calculate the best sensor mode we can use based on the user request. */<br>
> -       V4L2VideoDevice::Formats fmts = data->unicam_[Unicam::Image].dev()->formats();<br>
> -       V4L2DeviceFormat sensorFormat = findBestMode(fmts, rawStream ? sensorSize : maxSize);<br>
> +       V4L2SubdeviceFormat sensorFormat = findBestMode(data->sensorFormats_, rawStream ? sensorSize : maxSize);<br>
> +       V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(sensorFormat);<br>
> +<br>
> +       ret = data->sensor_->setFormat(&sensorFormat);<br>
> +       if (ret)<br>
> +               return ret;<br>
><br>
>         /*<br>
>          * Unicam image output format. The ISP input format gets set at start,<br>
>          * just in case we have swapped bayer orders due to flips.<br>
>          */<br>
> -       ret = data->unicam_[Unicam::Image].dev()->setFormat(&sensorFormat);<br>
> +       ret = data->unicam_[Unicam::Image].dev()->setFormat(&unicamFormat);<br>
>         if (ret)<br>
>                 return ret;<br>
><br>
> -       /*<br>
> -        * The control ranges associated with the sensor may need updating<br>
> -        * after a format change.<br>
> -        * \todo Use the CameraSensor::setFormat API instead.<br>
> -        */<br>
> -       data->sensor_->updateControlInfo();<br>
> -<br>
>         LOG(RPI, Info) << "Sensor: " << camera->id()<br>
> -                      << " - Selected mode: " << sensorFormat.toString();<br>
> +                      << " - Selected sensor mode: " << sensorFormat.toString()<br>
> +                      << " - Selected unicam mode: " << unicamFormat.toString();<br>
><br>
>         /*<br>
>          * This format may be reset on start() if the bayer order has changed<br>
>          * because of flips in the sensor.<br>
>          */<br>
> -       ret = data->isp_[Isp::Input].dev()->setFormat(&sensorFormat);<br>
> +       ret = data->isp_[Isp::Input].dev()->setFormat(&unicamFormat);<br>
>         if (ret)<br>
>                 return ret;<br>
><br>
> @@ -746,8 +767,8 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)<br>
>         data->ispMinCropSize_ = testCrop.size();<br>
><br>
>         /* Adjust aspect ratio by providing crops on the input image. */<br>
> -       Size size = sensorFormat.size.boundedToAspectRatio(maxSize);<br>
> -       Rectangle crop = size.centeredTo(Rectangle(sensorFormat.size).center());<br>
> +       Size size = unicamFormat.size.boundedToAspectRatio(maxSize);<br>
> +       Rectangle crop = size.centeredTo(Rectangle(unicamFormat.size).center());<br>
>         data->ispCrop_ = crop;<br>
><br>
>         data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);<br>
> @@ -761,8 +782,11 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)<br>
>          * supports it.<br>
>          */<br>
>         if (data->sensorMetadata_) {<br>
> -               format = {};<br>
> +               V4L2SubdeviceFormat embeddedFormat;<br>
> +<br>
> +               data->sensor_->device()->getFormat(1, &embeddedFormat);<br>
>                 format.fourcc = V4L2PixelFormat(V4L2_META_FMT_SENSOR_DATA);<br>
> +               format.planes[0].size = embeddedFormat.size.width * embeddedFormat.size.height;<br>
><br>
>                 LOG(RPI, Debug) << "Setting embedded data format.";<br>
>                 ret = data->unicam_[Unicam::Embedded].dev()->setFormat(&format);<br>
> @@ -847,9 +871,14 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)<br>
>          * IPA configure may have changed the sensor flips - hence the bayer<br>
>          * order. Get the sensor format and set the ISP input now.<br>
>          */<br>
> -       V4L2DeviceFormat sensorFormat;<br>
> -       data->unicam_[Unicam::Image].dev()->getFormat(&sensorFormat);<br>
> -       ret = data->isp_[Isp::Input].dev()->setFormat(&sensorFormat);<br>
> +       V4L2SubdeviceFormat sensorFormat;<br>
> +       data->sensor_->device()->getFormat(0, &sensorFormat);<br>
> +<br>
> +       V4L2DeviceFormat ispFormat;<br>
> +       ispFormat.fourcc = BayerFormat::fromMbusCode(sensorFormat.mbus_code).toV4L2PixelFormat();<br>
<br>
Generally there's the occasional assumption that fromMbusCode won't<br>
return invalid Bayer formats, and I guess we're comfortable with that?<br>
It certainly shouldn't be possible.<br></blockquote><div><br></div><div>Yes, I ought to add an assert to ensure the bayer format is valid.</div><div><br></div><div>Regards,</div><div>Naush</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
But it seems good to me, so:<br>
<br>
Reviewed-by: David Plowman <<a href="mailto:david.plowman@raspberrypi.com" target="_blank">david.plowman@raspberrypi.com</a>><br>
<br>
Thanks!<br>
David<br>
<br>
> +       ispFormat.size = sensorFormat.size;<br>
> +<br>
> +       ret = data->isp_[Isp::Input].dev()->setFormat(&ispFormat);<br>
>         if (ret) {<br>
>                 stop(camera);<br>
>                 return ret;<br>
> @@ -1004,6 +1033,8 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)<br>
>         if (data->sensor_->init())<br>
>                 return false;<br>
><br>
> +       data->sensorFormats_ = populateSensorFormats(data->sensor_);<br>
> +<br>
>         ipa::RPi::SensorConfig sensorConfig;<br>
>         if (data->loadIPA(&sensorConfig)) {<br>
>                 LOG(RPI, Error) << "Failed to load a suitable IPA library";<br>
> @@ -1030,6 +1061,11 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)<br>
>                         return false;<br>
>         }<br>
><br>
> +       if (!(data->unicam_[Unicam::Image].dev()->caps().device_caps() & V4L2_CAP_IO_MC)) {<br>
> +               LOG(RPI, Error) << "Unicam driver did not advertise V4L2_CAP_IO_MC, please update your kernel!";<br>
> +               return false;<br>
> +       }<br>
> +<br>
>         /*<br>
>          * Setup our delayed control writer with the sensor default<br>
>          * gain and exposure delays. Mark VBLANK for priority write.<br>
> @@ -1039,7 +1075,7 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)<br>
>                 { V4L2_CID_EXPOSURE, { sensorConfig.exposureDelay, false } },<br>
>                 { V4L2_CID_VBLANK, { sensorConfig.vblankDelay, true } }<br>
>         };<br>
> -       data->delayedCtrls_ = std::make_unique<DelayedControls>(data->unicam_[Unicam::Image].dev(), params);<br>
> +       data->delayedCtrls_ = std::make_unique<DelayedControls>(data->sensor_->device(), params);<br>
>         data->sensorMetadata_ = sensorConfig.sensorMetadata;<br>
><br>
>         /* Register the controls that the Raspberry Pi IPA can handle. */<br>
> @@ -1066,15 +1102,14 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)<br>
>          * As part of answering the final question, we reset the camera to<br>
>          * no transform at all.<br>
>          */<br>
> -<br>
> -       V4L2VideoDevice *dev = data->unicam_[Unicam::Image].dev();<br>
> -       const struct v4l2_query_ext_ctrl *hflipCtrl = dev->controlInfo(V4L2_CID_HFLIP);<br>
> +       const V4L2Subdevice *sensor = data->sensor_->device();<br>
> +       const struct v4l2_query_ext_ctrl *hflipCtrl = sensor->controlInfo(V4L2_CID_HFLIP);<br>
>         if (hflipCtrl) {<br>
>                 /* We assume it will support vflips too... */<br>
>                 data->supportsFlips_ = true;<br>
>                 data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT;<br>
><br>
> -               ControlList ctrls(dev->controls());<br>
> +               ControlList ctrls(data->sensor_->controls());<br>
>                 ctrls.set(V4L2_CID_HFLIP, 0);<br>
>                 ctrls.set(V4L2_CID_VFLIP, 0);<br>
>                 data->setSensorControls(ctrls);<br>
> @@ -1082,9 +1117,8 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator)<br>
><br>
>         /* Look for a valid Bayer format. */<br>
>         BayerFormat bayerFormat;<br>
> -       for (const auto &iter : dev->formats()) {<br>
> -               V4L2PixelFormat v4l2Format = iter.first;<br>
> -               bayerFormat = BayerFormat::fromV4L2PixelFormat(v4l2Format);<br>
> +       for (const auto &iter : data->sensorFormats_) {<br>
> +               bayerFormat = BayerFormat::fromMbusCode(iter.first);<br>
>                 if (bayerFormat.isValid())<br>
>                         break;<br>
>         }<br>
> @@ -1271,7 +1305,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)<br>
>                 }<br>
>         }<br>
><br>
> -       entityControls.emplace(0, unicam_[Unicam::Image].dev()->controls());<br>
> +       entityControls.emplace(0, sensor_->controls());<br>
>         entityControls.emplace(1, isp_[Isp::Input].dev()->controls());<br>
><br>
>         /* Always send the user transform to the IPA. */<br>
> @@ -1406,10 +1440,10 @@ void RPiCameraData::setSensorControls(ControlList &controls)<br>
>                 ControlList vblank_ctrl;<br>
><br>
>                 vblank_ctrl.set(V4L2_CID_VBLANK, controls.get(V4L2_CID_VBLANK));<br>
> -               unicam_[Unicam::Image].dev()->setControls(&vblank_ctrl);<br>
> +               sensor_->setControls(&vblank_ctrl);<br>
>         }<br>
><br>
> -       unicam_[Unicam::Image].dev()->setControls(&controls);<br>
> +       sensor_->setControls(&controls);<br>
>  }<br>
><br>
>  void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)<br>
> --<br>
> 2.25.1<br>
><br>
</blockquote></div></div>