[RFC,6/7] libcamera: software_isp: Add autofocus controls
diff mbox series

Message ID 20261021080000.16624-7-mzamazal@redhat.com
State Superseded
Headers show
Series
  • Autofocus basics for software ISP
Related show

Commit Message

Milan Zamazal Oct. 21, 2026, 7:59 a.m. UTC
When the camera supports autofocus, the lens controls are passed around
and autofocus values are read and written.

This is similar to what ipu3 pipeline already does.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
 include/libcamera/ipa/softisp.mojom                 |  3 ++-
 src/ipa/softisp/ipa_context.h                       |  1 +
 src/ipa/softisp/softisp.cpp                         |  7 ++++++-
 src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
 src/libcamera/software_isp/software_isp.cpp         |  4 ++--
 6 files changed, 24 insertions(+), 8 deletions(-)

Comments

Barnabás Pőcze Oct. 21, 2026, 2:42 p.m. UTC | #1
2026. 10. 21. 9:59 keltezéssel, Milan Zamazal írta:
> When the camera supports autofocus, the lens controls are passed around
> and autofocus values are read and written.
> 
> This is similar to what ipu3 pipeline already does.
> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>   .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>   include/libcamera/ipa/softisp.mojom                 |  3 ++-
>   src/ipa/softisp/ipa_context.h                       |  1 +
>   src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>   src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>   src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>   6 files changed, 24 insertions(+), 8 deletions(-)
> 
> [...]
> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
> index 209af1fde..37b8c0f11 100644
> --- a/include/libcamera/ipa/softisp.mojom
> +++ b/include/libcamera/ipa/softisp.mojom
> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>   
>   struct IPAConfigInfo {
>   	libcamera.ControlInfoMap sensorControls;
> +	libcamera.ControlInfoMap lensControls;
>   };
>   
>   interface IPASoftIspInterface {
> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>   };
>   
>   interface IPASoftIspEventInterface {
> -	setSensorControls(libcamera.ControlList sensorControls);
> +	setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);

I think a simple `int32_t` is greatly preferable to a whole control list,
after all, there is a single control, and I'm not sure if the overhead of
a `ControlList` is justifiable at the moment. And the lens presence is
already checked in the event handler, so I see no real drawbacks.


>   	paramsComputed(uint32 frame);
>   	metadataReady(uint32 frame, libcamera.ControlList metadata);
>   };
> [...]
Milan Zamazal Oct. 21, 2026, 2:57 p.m. UTC | #2
Hi Barnabás,

thank you for review.

Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:

> 2026. 10. 21. 9:59 keltezéssel, Milan Zamazal írta:
>> When the camera supports autofocus, the lens controls are passed around
>> and autofocus values are read and written.
>> This is similar to what ipu3 pipeline already does.
>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> ---
>>   .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>>   include/libcamera/ipa/softisp.mojom                 |  3 ++-
>>   src/ipa/softisp/ipa_context.h                       |  1 +
>>   src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>>   src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>>   src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>>   6 files changed, 24 insertions(+), 8 deletions(-)
>> [...]
>> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
>> index 209af1fde..37b8c0f11 100644
>> --- a/include/libcamera/ipa/softisp.mojom
>> +++ b/include/libcamera/ipa/softisp.mojom
>> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>>     struct IPAConfigInfo {
>>   	libcamera.ControlInfoMap sensorControls;
>> +	libcamera.ControlInfoMap lensControls;
>>   };
>>     interface IPASoftIspInterface {
>> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>>   };
>>     interface IPASoftIspEventInterface {
>> -	setSensorControls(libcamera.ControlList sensorControls);
>> +	setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
>
> I think a simple `int32_t` is greatly preferable to a whole control list,
> after all, there is a single control, and I'm not sure if the overhead of
> a `ControlList` is justifiable at the moment. And the lens presence is
> already checked in the event handler, so I see no real drawbacks.

Well, I mimicked what ipu3 pipeline does.  Does the overhead matter in
practice?  Should it be changed in ipu3 too?

>
>
>>   	paramsComputed(uint32 frame);
>>   	metadataReady(uint32 frame, libcamera.ControlList metadata);
>>   };
>> [...]
Barnabás Pőcze Oct. 21, 2026, 3:11 p.m. UTC | #3
2026. 10. 21. 16:57 keltezéssel, Milan Zamazal írta:
> Hi Barnabás,
> 
> thank you for review.
> 
> Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:
> 
>> 2026. 10. 21. 9:59 keltezéssel, Milan Zamazal írta:
>>> When the camera supports autofocus, the lens controls are passed around
>>> and autofocus values are read and written.
>>> This is similar to what ipu3 pipeline already does.
>>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>>> ---
>>>    .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>>>    include/libcamera/ipa/softisp.mojom                 |  3 ++-
>>>    src/ipa/softisp/ipa_context.h                       |  1 +
>>>    src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>>>    src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>>>    src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>>>    6 files changed, 24 insertions(+), 8 deletions(-)
>>> [...]
>>> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
>>> index 209af1fde..37b8c0f11 100644
>>> --- a/include/libcamera/ipa/softisp.mojom
>>> +++ b/include/libcamera/ipa/softisp.mojom
>>> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>>>      struct IPAConfigInfo {
>>>    	libcamera.ControlInfoMap sensorControls;
>>> +	libcamera.ControlInfoMap lensControls;
>>>    };
>>>      interface IPASoftIspInterface {
>>> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>>>    };
>>>      interface IPASoftIspEventInterface {
>>> -	setSensorControls(libcamera.ControlList sensorControls);
>>> +	setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
>>
>> I think a simple `int32_t` is greatly preferable to a whole control list,
>> after all, there is a single control, and I'm not sure if the overhead of
>> a `ControlList` is justifiable at the moment. And the lens presence is
>> already checked in the event handler, so I see no real drawbacks.
> 
> Well, I mimicked what ipu3 pipeline does.  Does the overhead matter in
> practice?  Should it be changed in ipu3 too?

Probably not, but why introduce overhead (and more complex code) when non is needed?
I would change ipu3 as well, but it should be a separate thing.


> 
>>
>>
>>>    	paramsComputed(uint32 frame);
>>>    	metadataReady(uint32 frame, libcamera.ControlList metadata);
>>>    };
>>> [...]
>
Milan Zamazal Oct. 21, 2026, 4:06 p.m. UTC | #4
Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:

> 2026. 10. 21. 16:57 keltezéssel, Milan Zamazal írta:
>> Hi Barnabás,
>> thank you for review.
>> Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:
>> 
>>> 2026. 10. 21. 9:59 keltezéssel, Milan Zamazal írta:
>>>> When the camera supports autofocus, the lens controls are passed around
>>>> and autofocus values are read and written.
>>>> This is similar to what ipu3 pipeline already does.
>>>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>>>> ---
>>>>    .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>>>>    include/libcamera/ipa/softisp.mojom                 |  3 ++-
>>>>    src/ipa/softisp/ipa_context.h                       |  1 +
>>>>    src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>>>>    src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>>>>    src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>>>>    6 files changed, 24 insertions(+), 8 deletions(-)
>>>> [...]
>>>> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
>>>> index 209af1fde..37b8c0f11 100644
>>>> --- a/include/libcamera/ipa/softisp.mojom
>>>> +++ b/include/libcamera/ipa/softisp.mojom
>>>> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>>>>      struct IPAConfigInfo {
>>>>    	libcamera.ControlInfoMap sensorControls;
>>>> +	libcamera.ControlInfoMap lensControls;
>>>>    };
>>>>      interface IPASoftIspInterface {
>>>> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>>>>    };
>>>>      interface IPASoftIspEventInterface {
>>>> -	setSensorControls(libcamera.ControlList sensorControls);
>>>> +	setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
>>>
>>> I think a simple `int32_t` is greatly preferable to a whole control list,
>>> after all, there is a single control, and I'm not sure if the overhead of
>>> a `ControlList` is justifiable at the moment. And the lens presence is
>>> already checked in the event handler, so I see no real drawbacks.
>> Well, I mimicked what ipu3 pipeline does.  Does the overhead matter in
>> practice?  Should it be changed in ipu3 too?
>
> Probably not, but why introduce overhead (and more complex code) when non is needed?

Clarity?  Hans's patches use this too, so it looks like a natural choice.

> I would change ipu3 as well, but it should be a separate thing.

I don't mind but let's see if there are other opinions.

>
>
>> 
>>>
>>>
>>>>    	paramsComputed(uint32 frame);
>>>>    	metadataReady(uint32 frame, libcamera.ControlList metadata);
>>>>    };
>>>> [...]
>>
Kieran Bingham Oct. 22, 2026, 4:26 p.m. UTC | #5
Quoting Milan Zamazal (2026-10-21 17:06:59)
> Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:
> 
> > 2026. 10. 21. 16:57 keltezéssel, Milan Zamazal írta:
> >> Hi Barnabás,
> >> thank you for review.
> >> Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes:
> >> 
> >>> 2026. 10. 21. 9:59 keltezéssel, Milan Zamazal írta:
> >>>> When the camera supports autofocus, the lens controls are passed around
> >>>> and autofocus values are read and written.
> >>>> This is similar to what ipu3 pipeline already does.
> >>>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> >>>> ---
> >>>>    .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
> >>>>    include/libcamera/ipa/softisp.mojom                 |  3 ++-
> >>>>    src/ipa/softisp/ipa_context.h                       |  1 +
> >>>>    src/ipa/softisp/softisp.cpp                         |  7 ++++++-
> >>>>    src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
> >>>>    src/libcamera/software_isp/software_isp.cpp         |  4 ++--
> >>>>    6 files changed, 24 insertions(+), 8 deletions(-)
> >>>> [...]
> >>>> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
> >>>> index 209af1fde..37b8c0f11 100644
> >>>> --- a/include/libcamera/ipa/softisp.mojom
> >>>> +++ b/include/libcamera/ipa/softisp.mojom
> >>>> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
> >>>>      struct IPAConfigInfo {
> >>>>            libcamera.ControlInfoMap sensorControls;
> >>>> +  libcamera.ControlInfoMap lensControls;
> >>>>    };
> >>>>      interface IPASoftIspInterface {
> >>>> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
> >>>>    };
> >>>>      interface IPASoftIspEventInterface {
> >>>> -  setSensorControls(libcamera.ControlList sensorControls);
> >>>> +  setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
> >>>
> >>> I think a simple `int32_t` is greatly preferable to a whole control list,
> >>> after all, there is a single control, and I'm not sure if the overhead of
> >>> a `ControlList` is justifiable at the moment. And the lens presence is
> >>> already checked in the event handler, so I see no real drawbacks.
> >> Well, I mimicked what ipu3 pipeline does.  Does the overhead matter in
> >> practice?  Should it be changed in ipu3 too?
> >
> > Probably not, but why introduce overhead (and more complex code) when non is needed?
> 
> Clarity?  Hans's patches use this too, so it looks like a natural choice.
> 
> > I would change ipu3 as well, but it should be a separate thing.
> 
> I don't mind but let's see if there are other opinions.

Are there other controls we might set on a lens/vcm? I'd probably keep
it as a ControlList to keep it the same as sensorControls.

We might end up having to determine between V4L2_CID_FOCUS_ABSOLUTE or
V4L2_CID_FOCUS_RELATIVE, or V4L2_CID_FOCUS_AUTO settings in the future.


Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

--
Kieran


> 
> >
> >
> >> 
> >>>
> >>>
> >>>>            paramsComputed(uint32 frame);
> >>>>            metadataReady(uint32 frame, libcamera.ControlList metadata);
> >>>>    };
> >>>> [...]
> >> 
>
Kieran Bingham Oct. 22, 2026, 4:39 p.m. UTC | #6
Quoting Milan Zamazal (2026-10-21 08:59:46)
> When the camera supports autofocus, the lens controls are passed around
> and autofocus values are read and written.
> 
> This is similar to what ipu3 pipeline already does.

I probably wouldn't add this line to the commit message, perhaps under
the ---.

One aspect I'd try to consider here is that even if there is no VCM/lens
controller - we should still be able to enable Af statistics and report
the metadata.

When you have no VCM - you might still need to do manual focus tuning of
the lens - (i.e. rotate the lens until you get the highest
variance/af-value).


> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
> ---
>  .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>  include/libcamera/ipa/softisp.mojom                 |  3 ++-
>  src/ipa/softisp/ipa_context.h                       |  1 +
>  src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>  src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>  src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>  6 files changed, 24 insertions(+), 8 deletions(-)
> 
> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
> index b8b24bb1b..4491226ce 100644
> --- a/include/libcamera/internal/software_isp/software_isp.h
> +++ b/include/libcamera/internal/software_isp/software_isp.h
> @@ -87,11 +87,11 @@ public:
>         Signal<FrameBuffer *> outputBufferReady;
>         Signal<uint32_t, uint32_t> ispStatsReady;
>         Signal<uint32_t, const ControlList &> metadataReady;
> -       Signal<const ControlList &> setSensorControls;
> +       Signal<const ControlList &, const ControlList &> setSensorControls;
>  
>  private:
>         void saveIspParams(uint32_t frame);
> -       void setSensorCtrls(const ControlList &sensorControls);
> +       void setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls);
>         void statsReady(uint32_t frame, uint32_t bufferId);
>         void inputReady(FrameBuffer *input);
>         void outputReady(FrameBuffer *output);
> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
> index 209af1fde..37b8c0f11 100644
> --- a/include/libcamera/ipa/softisp.mojom
> +++ b/include/libcamera/ipa/softisp.mojom
> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>  
>  struct IPAConfigInfo {
>         libcamera.ControlInfoMap sensorControls;
> +       libcamera.ControlInfoMap lensControls;
>  };
>  
>  interface IPASoftIspInterface {
> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>  };
>  
>  interface IPASoftIspEventInterface {
> -       setSensorControls(libcamera.ControlList sensorControls);
> +       setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
>         paramsComputed(uint32 frame);
>         metadataReady(uint32 frame, libcamera.ControlList metadata);
>  };
> diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h
> index 8546dedd8..8e9ef3850 100644
> --- a/src/ipa/softisp/ipa_context.h
> +++ b/src/ipa/softisp/ipa_context.h
> @@ -80,6 +80,7 @@ struct IPAContext {
>  
>         IPACameraSensorInfo sensorInfo;
>         ControlInfoMap sensorControls;
> +       ControlInfoMap lensControls;
>         std::unique_ptr<CameraSensorHelper> camHelper;
>         IPASessionConfiguration configuration;
>         IPAActiveState activeState;
> diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp
> index 8acfcd1a0..daee794b2 100644
> --- a/src/ipa/softisp/softisp.cpp
> +++ b/src/ipa/softisp/softisp.cpp
> @@ -187,6 +187,7 @@ int IPASoftIsp::init(const IPASettings &settings,
>  int IPASoftIsp::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ipaControls)
>  {
>         context_.sensorControls = configInfo.sensorControls;
> +       context_.lensControls = configInfo.lensControls;
>  
>         /* Clear the IPA context before the streaming session. */
>         context_.configuration = {};
> @@ -251,7 +252,11 @@ void IPASoftIsp::processStats(const uint32_t frame,
>         ControlList ctrls(context_.sensorControls);
>         agc::prepareControls(ctrls, context_.camHelper.get(),
>                              frameContext.agc.exposure, frameContext.agc.gain);
> -       setSensorControls.emit(ctrls);
> +       ControlList lensCtrls(context_.lensControls);
> +       if (context_.afEnabled)
> +               lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE,
> +                             static_cast<int32_t>(context_.activeState.af.focus));

Some how I thought the af.cpp would be setting the control list, and in
control of things.

What happens with exposure and gain controls? Are they mapped by the IPA
or the libipa::agc ?


> +       setSensorControls.emit(ctrls, lensCtrls);
>  }
>  
>  std::string IPASoftIsp::logPrefix() const
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 35c29ceca..a55672cb4 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -33,6 +33,7 @@
>  #include <libcamera/stream.h>
>  
>  #include "libcamera/internal/camera.h"
> +#include "libcamera/internal/camera_lens.h"
>  #include "libcamera/internal/camera_manager.h"
>  #include "libcamera/internal/camera_sensor.h"
>  #include "libcamera/internal/camera_sensor_properties.h"
> @@ -371,7 +372,7 @@ private:
>  
>         void ispStatsReady(uint32_t frame, uint32_t bufferId);
>         void metadataReady(uint32_t frame, const ControlList &metadata);
> -       void setSensorControls(const ControlList &sensorControls);
> +       void setSensorControls(const ControlList &sensorControls, const ControlList &lensControls);
>  };
>  
>  class SimpleCameraConfiguration : public CameraConfiguration
> @@ -1041,7 +1042,7 @@ void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata
>         tryCompleteRequest(info->request);
>  }
>  
> -void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
> +void SimpleCameraData::setSensorControls(const ControlList &sensorControls, const ControlList &lensControls)
>  {
>         delayedCtrls_->push(sensorControls);
>         /*
> @@ -1055,6 +1056,12 @@ void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
>         if (!frameStartEmitter_) {
>                 ControlList ctrls(sensorControls);
>                 sensor_->setControls(&ctrls);
> +
> +               CameraLens *lens = sensor_->focusLens();
> +               if (lens && lensControls.contains(V4L2_CID_FOCUS_ABSOLUTE)) {
> +                       ControlValue const &focusValue = lensControls.get(V4L2_CID_FOCUS_ABSOLUTE);
> +                       lens->setFocusPosition(focusValue.get<int32_t>());

is there a lens->setControls() to just give the whole list over ?

I see Barnaba's point here about the overhead of wrapping the position
now. But I still suspect some devices might have more complexity, which
we shouldn't duplicate to each pipeline handler ... not sure yet.


> +               }
>         }
>  }
>  
> @@ -1605,6 +1612,8 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
>         } else {
>                 ipa::softisp::IPAConfigInfo configInfo;
>                 configInfo.sensorControls = data->sensor_->controls();
> +               if (data->sensor_->focusLens())
> +                       configInfo.lensControls = data->sensor_->focusLens()->controls();
>                 return data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_);
>         }
>  }
> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> index a40976055..c10445d72 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -442,9 +442,9 @@ void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t frame)
>         debayerParams_ = *sharedParams_;
>  }
>  
> -void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
> +void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls)
>  {
> -       setSensorControls.emit(sensorControls);
> +       setSensorControls.emit(sensorControls, lensControls);
>  }
>  
>  void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)
> -- 
> 2.56.0
>
Milan Zamazal Oct. 22, 2026, 7:25 p.m. UTC | #7
Hi Kieran,

thank you for review.

Kieran Bingham <kieran.bingham@ideasonboard.com> writes:

> Quoting Milan Zamazal (2026-10-21 08:59:46)
>> When the camera supports autofocus, the lens controls are passed around
>> and autofocus values are read and written.
>> 
>> This is similar to what ipu3 pipeline already does.
>
> I probably wouldn't add this line to the commit message, perhaps under
> the ---.

OK.

>
> One aspect I'd try to consider here is that even if there is no VCM/lens
> controller - we should still be able to enable Af statistics and report
> the metadata.
>
> When you have no VCM - you might still need to do manual focus tuning of
> the lens - (i.e. rotate the lens until you get the highest
> variance/af-value).

Makes sense.

>
>
>> 
>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
>> ---
>>  .../libcamera/internal/software_isp/software_isp.h  |  4 ++--
>>  include/libcamera/ipa/softisp.mojom                 |  3 ++-
>>  src/ipa/softisp/ipa_context.h                       |  1 +
>>  src/ipa/softisp/softisp.cpp                         |  7 ++++++-
>>  src/libcamera/pipeline/simple/simple.cpp            | 13 +++++++++++--
>>  src/libcamera/software_isp/software_isp.cpp         |  4 ++--
>>  6 files changed, 24 insertions(+), 8 deletions(-)
>> 
>> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
>> index b8b24bb1b..4491226ce 100644
>> --- a/include/libcamera/internal/software_isp/software_isp.h
>> +++ b/include/libcamera/internal/software_isp/software_isp.h
>> @@ -87,11 +87,11 @@ public:
>>         Signal<FrameBuffer *> outputBufferReady;
>>         Signal<uint32_t, uint32_t> ispStatsReady;
>>         Signal<uint32_t, const ControlList &> metadataReady;
>> -       Signal<const ControlList &> setSensorControls;
>> +       Signal<const ControlList &, const ControlList &> setSensorControls;
>>  
>>  private:
>>         void saveIspParams(uint32_t frame);
>> -       void setSensorCtrls(const ControlList &sensorControls);
>> +       void setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls);
>>         void statsReady(uint32_t frame, uint32_t bufferId);
>>         void inputReady(FrameBuffer *input);
>>         void outputReady(FrameBuffer *output);
>> diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
>> index 209af1fde..37b8c0f11 100644
>> --- a/include/libcamera/ipa/softisp.mojom
>> +++ b/include/libcamera/ipa/softisp.mojom
>> @@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
>>  
>>  struct IPAConfigInfo {
>>         libcamera.ControlInfoMap sensorControls;
>> +       libcamera.ControlInfoMap lensControls;
>>  };
>>  
>>  interface IPASoftIspInterface {
>> @@ -32,7 +33,7 @@ interface IPASoftIspInterface {
>>  };
>>  
>>  interface IPASoftIspEventInterface {
>> -       setSensorControls(libcamera.ControlList sensorControls);
>> +       setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
>>         paramsComputed(uint32 frame);
>>         metadataReady(uint32 frame, libcamera.ControlList metadata);
>>  };
>> diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h
>> index 8546dedd8..8e9ef3850 100644
>> --- a/src/ipa/softisp/ipa_context.h
>> +++ b/src/ipa/softisp/ipa_context.h
>> @@ -80,6 +80,7 @@ struct IPAContext {
>>  
>>         IPACameraSensorInfo sensorInfo;
>>         ControlInfoMap sensorControls;
>> +       ControlInfoMap lensControls;
>>         std::unique_ptr<CameraSensorHelper> camHelper;
>>         IPASessionConfiguration configuration;
>>         IPAActiveState activeState;
>> diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp
>> index 8acfcd1a0..daee794b2 100644
>> --- a/src/ipa/softisp/softisp.cpp
>> +++ b/src/ipa/softisp/softisp.cpp
>> @@ -187,6 +187,7 @@ int IPASoftIsp::init(const IPASettings &settings,
>>  int IPASoftIsp::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ipaControls)
>>  {
>>         context_.sensorControls = configInfo.sensorControls;
>> +       context_.lensControls = configInfo.lensControls;
>>  
>>         /* Clear the IPA context before the streaming session. */
>>         context_.configuration = {};
>> @@ -251,7 +252,11 @@ void IPASoftIsp::processStats(const uint32_t frame,
>>         ControlList ctrls(context_.sensorControls);
>>         agc::prepareControls(ctrls, context_.camHelper.get(),
>>                              frameContext.agc.exposure, frameContext.agc.gain);
>> -       setSensorControls.emit(ctrls);
>> +       ControlList lensCtrls(context_.lensControls);
>> +       if (context_.afEnabled)
>> +               lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE,
>> +                             static_cast<int32_t>(context_.activeState.af.focus));
>
> Some how I thought the af.cpp would be setting the control list, and in
> control of things.
>
> What happens with exposure and gain controls? Are they mapped by the IPA
> or the libipa::agc ?

Yes, they are, in prepareControls called above.  Let's do the same with
lensCtrls.

>
>
>> +       setSensorControls.emit(ctrls, lensCtrls);
>>  }
>>  
>>  std::string IPASoftIsp::logPrefix() const
>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
>> index 35c29ceca..a55672cb4 100644
>> --- a/src/libcamera/pipeline/simple/simple.cpp
>> +++ b/src/libcamera/pipeline/simple/simple.cpp
>> @@ -33,6 +33,7 @@
>>  #include <libcamera/stream.h>
>>  
>>  #include "libcamera/internal/camera.h"
>> +#include "libcamera/internal/camera_lens.h"
>>  #include "libcamera/internal/camera_manager.h"
>>  #include "libcamera/internal/camera_sensor.h"
>>  #include "libcamera/internal/camera_sensor_properties.h"
>> @@ -371,7 +372,7 @@ private:
>>  
>>         void ispStatsReady(uint32_t frame, uint32_t bufferId);
>>         void metadataReady(uint32_t frame, const ControlList &metadata);
>> -       void setSensorControls(const ControlList &sensorControls);
>> +       void setSensorControls(const ControlList &sensorControls, const ControlList &lensControls);
>>  };
>>  
>>  class SimpleCameraConfiguration : public CameraConfiguration
>> @@ -1041,7 +1042,7 @@ void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata
>>         tryCompleteRequest(info->request);
>>  }
>>  
>> -void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
>> +void SimpleCameraData::setSensorControls(const ControlList &sensorControls, const ControlList &lensControls)
>>  {
>>         delayedCtrls_->push(sensorControls);
>>         /*
>> @@ -1055,6 +1056,12 @@ void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
>>         if (!frameStartEmitter_) {
>>                 ControlList ctrls(sensorControls);
>>                 sensor_->setControls(&ctrls);
>> +
>> +               CameraLens *lens = sensor_->focusLens();
>> +               if (lens && lensControls.contains(V4L2_CID_FOCUS_ABSOLUTE)) {
>> +                       ControlValue const &focusValue = lensControls.get(V4L2_CID_FOCUS_ABSOLUTE);
>> +                       lens->setFocusPosition(focusValue.get<int32_t>());
>
> is there a lens->setControls() to just give the whole list over ?

It's not.

>
> I see Barnaba's point here about the overhead of wrapping the position
> now. But I still suspect some devices might have more complexity, which
> we shouldn't duplicate to each pipeline handler ... not sure yet.
>
>
>> +               }
>>         }
>>  }
>>  
>> @@ -1605,6 +1612,8 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
>>         } else {
>>                 ipa::softisp::IPAConfigInfo configInfo;
>>                 configInfo.sensorControls = data->sensor_->controls();
>> +               if (data->sensor_->focusLens())
>> +                       configInfo.lensControls = data->sensor_->focusLens()->controls();
>>                 return data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_);
>>         }
>>  }
>> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
>> index a40976055..c10445d72 100644
>> --- a/src/libcamera/software_isp/software_isp.cpp
>> +++ b/src/libcamera/software_isp/software_isp.cpp
>> @@ -442,9 +442,9 @@ void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t frame)
>>         debayerParams_ = *sharedParams_;
>>  }
>>  
>> -void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
>> +void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls)
>>  {
>> -       setSensorControls.emit(sensorControls);
>> +       setSensorControls.emit(sensorControls, lensControls);
>>  }
>>  
>>  void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)
>> -- 
>> 2.56.0
>>

Patch
diff mbox series

diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index b8b24bb1b..4491226ce 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -87,11 +87,11 @@  public:
 	Signal<FrameBuffer *> outputBufferReady;
 	Signal<uint32_t, uint32_t> ispStatsReady;
 	Signal<uint32_t, const ControlList &> metadataReady;
-	Signal<const ControlList &> setSensorControls;
+	Signal<const ControlList &, const ControlList &> setSensorControls;
 
 private:
 	void saveIspParams(uint32_t frame);
-	void setSensorCtrls(const ControlList &sensorControls);
+	void setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls);
 	void statsReady(uint32_t frame, uint32_t bufferId);
 	void inputReady(FrameBuffer *input);
 	void outputReady(FrameBuffer *output);
diff --git a/include/libcamera/ipa/softisp.mojom b/include/libcamera/ipa/softisp.mojom
index 209af1fde..37b8c0f11 100644
--- a/include/libcamera/ipa/softisp.mojom
+++ b/include/libcamera/ipa/softisp.mojom
@@ -10,6 +10,7 @@  import "include/libcamera/ipa/core.mojom";
 
 struct IPAConfigInfo {
 	libcamera.ControlInfoMap sensorControls;
+	libcamera.ControlInfoMap lensControls;
 };
 
 interface IPASoftIspInterface {
@@ -32,7 +33,7 @@  interface IPASoftIspInterface {
 };
 
 interface IPASoftIspEventInterface {
-	setSensorControls(libcamera.ControlList sensorControls);
+	setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
 	paramsComputed(uint32 frame);
 	metadataReady(uint32 frame, libcamera.ControlList metadata);
 };
diff --git a/src/ipa/softisp/ipa_context.h b/src/ipa/softisp/ipa_context.h
index 8546dedd8..8e9ef3850 100644
--- a/src/ipa/softisp/ipa_context.h
+++ b/src/ipa/softisp/ipa_context.h
@@ -80,6 +80,7 @@  struct IPAContext {
 
 	IPACameraSensorInfo sensorInfo;
 	ControlInfoMap sensorControls;
+	ControlInfoMap lensControls;
 	std::unique_ptr<CameraSensorHelper> camHelper;
 	IPASessionConfiguration configuration;
 	IPAActiveState activeState;
diff --git a/src/ipa/softisp/softisp.cpp b/src/ipa/softisp/softisp.cpp
index 8acfcd1a0..daee794b2 100644
--- a/src/ipa/softisp/softisp.cpp
+++ b/src/ipa/softisp/softisp.cpp
@@ -187,6 +187,7 @@  int IPASoftIsp::init(const IPASettings &settings,
 int IPASoftIsp::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ipaControls)
 {
 	context_.sensorControls = configInfo.sensorControls;
+	context_.lensControls = configInfo.lensControls;
 
 	/* Clear the IPA context before the streaming session. */
 	context_.configuration = {};
@@ -251,7 +252,11 @@  void IPASoftIsp::processStats(const uint32_t frame,
 	ControlList ctrls(context_.sensorControls);
 	agc::prepareControls(ctrls, context_.camHelper.get(),
 			     frameContext.agc.exposure, frameContext.agc.gain);
-	setSensorControls.emit(ctrls);
+	ControlList lensCtrls(context_.lensControls);
+	if (context_.afEnabled)
+		lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE,
+			      static_cast<int32_t>(context_.activeState.af.focus));
+	setSensorControls.emit(ctrls, lensCtrls);
 }
 
 std::string IPASoftIsp::logPrefix() const
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 35c29ceca..a55672cb4 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -33,6 +33,7 @@ 
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/camera.h"
+#include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/camera_manager.h"
 #include "libcamera/internal/camera_sensor.h"
 #include "libcamera/internal/camera_sensor_properties.h"
@@ -371,7 +372,7 @@  private:
 
 	void ispStatsReady(uint32_t frame, uint32_t bufferId);
 	void metadataReady(uint32_t frame, const ControlList &metadata);
-	void setSensorControls(const ControlList &sensorControls);
+	void setSensorControls(const ControlList &sensorControls, const ControlList &lensControls);
 };
 
 class SimpleCameraConfiguration : public CameraConfiguration
@@ -1041,7 +1042,7 @@  void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata
 	tryCompleteRequest(info->request);
 }
 
-void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
+void SimpleCameraData::setSensorControls(const ControlList &sensorControls, const ControlList &lensControls)
 {
 	delayedCtrls_->push(sensorControls);
 	/*
@@ -1055,6 +1056,12 @@  void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
 	if (!frameStartEmitter_) {
 		ControlList ctrls(sensorControls);
 		sensor_->setControls(&ctrls);
+
+		CameraLens *lens = sensor_->focusLens();
+		if (lens && lensControls.contains(V4L2_CID_FOCUS_ABSOLUTE)) {
+			ControlValue const &focusValue = lensControls.get(V4L2_CID_FOCUS_ABSOLUTE);
+			lens->setFocusPosition(focusValue.get<int32_t>());
+		}
 	}
 }
 
@@ -1605,6 +1612,8 @@  int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 	} else {
 		ipa::softisp::IPAConfigInfo configInfo;
 		configInfo.sensorControls = data->sensor_->controls();
+		if (data->sensor_->focusLens())
+			configInfo.lensControls = data->sensor_->focusLens()->controls();
 		return data->swIsp_->configure(inputCfg, outputCfgs, configInfo, &data->controlInfo_);
 	}
 }
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index a40976055..c10445d72 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -442,9 +442,9 @@  void SoftwareIsp::saveIspParams([[maybe_unused]] uint32_t frame)
 	debayerParams_ = *sharedParams_;
 }
 
-void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
+void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls)
 {
-	setSensorControls.emit(sensorControls);
+	setSensorControls.emit(sensorControls, lensControls);
 }
 
 void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)