Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH 04/10] clk: qcom: clk-rpm: add msm8960 compatible
From: Konrad Dybcio @ 2026-04-14  8:08 UTC (permalink / raw)
  To: linux, Bjorn Andersson, Michael Turquette, Stephen Boyd,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
	Konrad Dybcio
  Cc: Krzysztof Kozlowski, linux-arm-msm, linux-clk, devicetree,
	linux-kernel, phone-devel, Rudraksha Gupta
In-Reply-To: <20260414-msm8960-wifi-v1-4-007fda9d6134@smankusors.com>

On 4/13/26 8:55 PM, Antony Kurniawan Soemardi via B4 Relay wrote:
> From: Antony Kurniawan Soemardi <linux@smankusors.com>
> 
> Add support for the "qcom,rpmcc-msm8960" compatible string to the
> RPM clock driver.
> 
> msm8960 uses the same RPM clock descriptions as apq8064, so reuse
> rpm_clk_apq8064 for this compatible.
> 
> Tested-by: Rudraksha Gupta <guptarud@gmail.com>
> Signed-off-by: Antony Kurniawan Soemardi <linux@smankusors.com>
> ---

This suggests a fallback compatible could be fitting

Konrad

^ permalink raw reply

* Re: [PATCH v8 0/9] riscv: spacemit: enable SD card support with UHS modes for OrangePi RV2
From: Troy Mitchell @ 2026-04-14  8:13 UTC (permalink / raw)
  To: Iker Pedrosa, Krzysztof Kozlowski
  Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Adrian Hunter, Paul Walmsley, Palmer Dabbelt, Albert Ou,
	Alexandre Ghiti, Yixun Lan, Troy Mitchell, Michael Opdenacker,
	Javier Martinez Canillas, linux-mmc, devicetree, linux-riscv,
	spacemit, linux-kernel, Anand Moon, Trevor Gamblin,
	Vincent Legoll
In-Reply-To: <CABdCQ=NhTkGJUh_fKnZoQMzdpyO-UbV5zrSfiNVUC7bkSBifTA@mail.gmail.com>

On Tue Apr 14, 2026 at 3:12 PM CST, Iker Pedrosa wrote:
> El lun, 13 abr 2026 a las 10:07, Krzysztof Kozlowski
> (<krzk@kernel.org>) escribió:
>>
>> On 13/04/2026 10:02, Iker Pedrosa wrote:
>> > This series enables complete SD card support for the Spacemit K1-based
>> > OrangePi RV2 board, including UHS (Ultra High Speed) modes for
>> > high-performance SD card operation.
>> >
>> > Background
>> >
>> > The Spacemit K1 SoC includes an SDHCI controller capable of supporting
>> > SD cards up to UHS-I speeds (SDR104 at 208MHz). However, mainline
>> > currently lacks basic SD controller configuration, SDHCI driver
>> > enhancements for voltage switching and tuning, and power management
>> > infrastructure.
>> >
>> > Implementation
>> >
>> > The series enables SD card support through coordinated layers:
>> >
>> > - Hardware infrastructure (patches 1-2): Device tree bindings for voltage
>> > switching hardware and essential clock infrastructure.
>> > - SDHCI driver enhancements (patches 3-7): Regulator framework
>> > integration, pinctrl state switching for voltage domains, AIB register
>> > programming, and comprehensive SDR tuning support for reliable UHS
>> > operation.
>> > - SoC and board integration (patches 8-10): Complete K1 SoC controller
>> > definitions, PMIC power infrastructure, and OrangePi RV2 board enablement
>> > with full UHS support.
>> >
>> > This transforms the OrangePi RV2 from having no SD card support to full
>> > UHS-I capability, enabling high-performance storage up to 208MHz.
>> >
>> > Tested-by: Michael Opdenacker <michael.opdenacker@rootcommit.com>
>> > Signed-off-by: Iker Pedrosa <ikerpedrosam@gmail.com>
>> > ---
>> > Changes in v8:
>> > - Resending the series as v8. The v7 submission failed due to an SMTP
>> >   error during transit, which resulted in a broken thread on the mailing
>> >   list.
>>
>> Hm? Everything is here:
>> https://lore.kernel.org/all/20260413-orangepi-sd-card-uhs-v7-1-16650f49c022@gmail.com/
>>
>> You can send individual patches to fix up threading, use --in-reply-to.
>
> My apologies for the noise and the rapid resend.
>
> The reason for v8 was that the v7 cover letter (0/9) failed to reach
> the mailing list due to an SMTP error on my end. This left the v7
> thread "headless" in the archives without the changelog or the full
> context of the series. I was attempting to fix the threading
> immediately so that reviewers would have a complete set of patches to
> look at, but I realize now that resending the entire series on the
> same day was premature.
So that's why Krzysztof said you should send individual patch with --in-reply-to.

                                      - Troy

^ permalink raw reply

* Re: [PATCH 01/35] dt-bindings: qcom,pdc: Tighten reg to single APSS DRV region
From: Mukesh Ojha @ 2026-04-14  8:24 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Dmitry Baryshkov, Thomas Gleixner, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	cros-qcom-dts-watchers, linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <ecb75ada-60c4-40e6-81bd-fc392007e9d8@oss.qualcomm.com>

On Mon, Apr 13, 2026 at 10:23:59AM +0200, Konrad Dybcio wrote:
> On 4/11/26 4:32 PM, Dmitry Baryshkov wrote:
> > On Sat, Apr 11, 2026 at 12:10:38AM +0530, Mukesh Ojha wrote:
> >> The PDC has multiple DRV regions, each sized 0x10000, where each region
> >> serves a specific client in the system. Linux only needs access to the
> > 
> > Nit: there are other OS than Linux. Would you rather point out that
> > other DRV regions are to be used by ... what?
> 
> TZ, HYP, HLOS, CPUCP..

Thanks for pitching in..

> 
> I'm wondering if we can make use of the HYP one on e.g. Glymur, to
> parallelize accesses (and whether that would bring any practical
> benefit).

I mean, Ideally, It makes sense to utilize extra 0x10000 to use or just
to use the hypervisor one for Glymur.

 
> In the RPMH architecture, each "client" has their own (GPU, AOP, DISP,
> etc.). Then, each one of those clients may have an associates RSC
> (Resource State Coordinator) and/or anyOf BCM ("interconnect"), VRM
> ("regulator"), ARC ("RPMHPD") voting interfaces
> 

> Konrad

-- 
-Mukesh Ojha

^ permalink raw reply

* Re: [PATCH v7 0/8] Add support for handling PCIe M.2 Key E connectors in devicetree
From: Andy Shevchenko @ 2026-04-14  8:28 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Manivannan Sadhasivam, Manivannan Sadhasivam, Rob Herring,
	Greg Kroah-Hartman, Jiri Slaby, Nathan Chancellor, Nicolas Schier,
	Hans de Goede, Ilpo Järvinen, Mark Pearson, Derek J. Clark,
	Krzysztof Kozlowski, Conor Dooley, Marcel Holtmann,
	Luiz Augusto von Dentz, Bartosz Golaszewski, Bartosz Golaszewski,
	linux-serial, linux-kernel, linux-kbuild, platform-driver-x86,
	linux-pci, devicetree, linux-arm-msm, linux-bluetooth, linux-pm,
	Stephan Gerhold, Dmitry Baryshkov, linux-acpi, Hans de Goede,
	Bartosz Golaszewski
In-Reply-To: <CAGXv+5E=tujhtZjwi6Qm7hk3Ks74UzTQHWq82NiTEw1+vYod5g@mail.gmail.com>

On Tue, Apr 14, 2026 at 01:03:19PM +0800, Chen-Yu Tsai wrote:
> On Tue, Apr 14, 2026 at 12:08 AM Manivannan Sadhasivam <mani@kernel.org> wrote:
> > On Mon, Apr 13, 2026 at 07:33:12PM +0530, Manivannan Sadhasivam wrote:
> > > On Mon, Apr 13, 2026 at 03:54:59PM +0800, Chen-Yu Tsai wrote:
> > > > On Thu, Mar 26, 2026 at 01:36:28PM +0530, Manivannan Sadhasivam wrote:

...

> > > > - Given that this connector actually represents two devices, how do I
> > > >   say I want the BT part to be a wakeup source, but not the WiFi part?
> > > >   Does wakeup-source even work at this point?
> > >
> > > You can't use the DT property since the devices are not described in DT
> > > statically. But you can still use the per-device 'wakeup' sysfs knob to enable
> > > wakeup.
> 
> I see. I think not being able to specify generic properties for the devices
> on the connector is going to be a bit problematic.

This is nature of the open-connectors, especially on the busses that are
hotpluggable, like PCIe. We never know what is connected there _ahead_.

In other words you can't describe in DT something that may not exist.

> requires specifying a bounce buffer / SWIOTLB for the PCIe WiFi card. The
> PCIe controller does not have an IOMMU behind it.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v4 2/5] media: iris: Add hardware power on/off ops for X1P42100
From: Wangao Wang @ 2026-04-14  8:32 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: wangao.wang, Bryan O'Donoghue, Vikash Garodia,
	Dikshita Agarwal, Abhinav Kumar, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, linux-media, linux-arm-msm, devicetree,
	linux-kernel
In-Reply-To: <fc1a1d5d-7802-4584-87af-db7ba7343b85@kernel.org>



On 2026/4/8 18:41, Krzysztof Kozlowski wrote:
> On 08/04/2026 11:16, Wangao Wang wrote:
>>
>>
>> On 2026/4/2 15:08, Krzysztof Kozlowski wrote:
>>>
>>> Why no IRIS_HW_AHB_CLK in power on sequence?
>>>
>>> So if you rewrite the code that you have list of clocks for hw power on
>>> (IRIS_HW_CLK + IRIS_HW_AHB_CLK for all variants, +IRIS_BSE_HW_CLK on
>>> this variant) you could have just one function for all of them and
>>> devices will be fully compatible.
>>>
>>> No?
>>>
>> The original patch was to add the IRIS_BSE_HW_CLK operation into the
>> common API, but Dmitry requested to separate Purwa's implementation out
>> independently.
> 
> So you don't know why you are doing things? I don't understand what sort
> of argument is that.
> 
> You are duplicating code, while all this is simply the same logic -
> three clocks which need to be enabled.
> 
> Best regards,
> Krzysztof

Hi Dmitry,

I think this can be moved into the common code, so that any future 
VPU3-related changes will also be applied to purwa. What do you think? 
If that works, I’ll update it in the next version.

-- 
Best Regards,
Wangao


^ permalink raw reply

* [PATCH v2 0/2] Configuring DMA threshold value for DW-MMC controllers
From: Kaustabh Chakraborty @ 2026-04-14  8:35 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin, Krzysztof Kozlowski, Alim Akhtar
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-kernel,
	linux-samsung-soc, Kaustabh Chakraborty

In Samsung Exynos 7870 devices with Broadcom Wi-Fi, it has been observed
that small sized DMA transfers are unreliable and are not written
properly, which renders the cache incoherent.

Experimental observations say that DMA transfer sizes of somewhere
around 64 to 512 are intolerable. We must thus implement a mechanism to
fall back to PIO transfer in this case. One such approach, which this
series implements is allowing the DMA transfer threshold, which is
already defined in the driver, to be configurable.

Note that this patch is likely to be labelled as a workaround. These
smaller transfers seem to be successful from downstream kernels,
however efforts to figure out how so went in vain. It is also very
possible that the downstream Broadcom Wi-Fi SDIO driver uses PIO
transfers as well.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
Changes in v2:
- Remove dt-binding to set DMA threshold (Krzysztof Kozlowski)
- Add comment to describe struct dw_mci::dma_threshold (Shawn Lin)
- Set DMA threshold in Exynos 7870 DW-MMC driver (Krzysztof Kozlowski)
- Link to v1: https://lore.kernel.org/r/20260412-dwmmc-dma-thr-v1-0-75a2f658eee3@disroot.org

---
Kaustabh Chakraborty (2):
      mmc: dw_mmc: implement option for configuring DMA threshold
      mmc: dw_mmc: exynos: increase DMA threshold value for exynos7870

 drivers/mmc/host/dw_mmc-exynos.c | 1 +
 drivers/mmc/host/dw_mmc.c        | 5 +++--
 drivers/mmc/host/dw_mmc.h        | 2 ++
 3 files changed, 6 insertions(+), 2 deletions(-)
---
base-commit: 1c7cc4904160c6fc6377564140062d68a3dc93a0
change-id: 20260412-dwmmc-dma-thr-1090d8285ea7

Best regards,
-- 
Kaustabh Chakraborty <kauschluss@disroot.org>


^ permalink raw reply

* [PATCH v2 1/2] mmc: dw_mmc: implement option for configuring DMA threshold
From: Kaustabh Chakraborty @ 2026-04-14  8:36 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin, Krzysztof Kozlowski, Alim Akhtar
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-kernel,
	linux-samsung-soc, Kaustabh Chakraborty
In-Reply-To: <20260414-dwmmc-dma-thr-v2-0-4058078f5361@disroot.org>

Some controllers, such as certain Exynos SDIO ones, are unable to
perform DMA transfers of small amount of bytes properly. Following the
device tree schema, implement the property to define the DMA transfer
threshold (from a hard coded value of 16 bytes) so that lesser number of
bytes can be transferred safely skipping DMA in such controllers. The
value of 16 bytes stays as the default for controllers which do not
define it. This value can be overridden by implementation-specific init
sequences.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mmc/host/dw_mmc.c | 5 +++--
 drivers/mmc/host/dw_mmc.h | 2 ++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 20193ee7b73eb..9dd9fed4ccf49 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -40,7 +40,6 @@
 				 SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
 #define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
 				 DW_MCI_CMD_ERROR_FLAGS)
-#define DW_MCI_DMA_THRESHOLD	16
 
 #define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
 #define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
@@ -821,7 +820,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
 	 * non-word-aligned buffers or lengths. Also, we don't bother
 	 * with all the DMA setup overhead for short transfers.
 	 */
-	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+	if (data->blocks * data->blksz < host->dma_threshold)
 		return -EINVAL;
 
 	if (data->blksz & 3)
@@ -3245,6 +3244,8 @@ int dw_mci_probe(struct dw_mci *host)
 		goto err_clk_ciu;
 	}
 
+	host->dma_threshold = 16;
+
 	if (host->rstc) {
 		reset_control_assert(host->rstc);
 		usleep_range(10, 50);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 42e58be74ce09..fc7601fba849f 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -164,6 +164,8 @@ struct dw_mci {
 	void __iomem		*fifo_reg;
 	u32			data_addr_override;
 	bool			wm_aligned;
+	/* Configurable data byte threshold value for DMA transfer. */
+	u32			dma_threshold;
 
 	struct scatterlist	*sg;
 	struct sg_mapping_iter	sg_miter;

-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 2/2] mmc: dw_mmc: exynos: increase DMA threshold value for exynos7870
From: Kaustabh Chakraborty @ 2026-04-14  8:36 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jaehoon Chung, Shawn Lin, Krzysztof Kozlowski, Alim Akhtar
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-kernel,
	linux-samsung-soc, Kaustabh Chakraborty
In-Reply-To: <20260414-dwmmc-dma-thr-v2-0-4058078f5361@disroot.org>

Exynos 7870 compatible controllers, such as SDIO ones are not able to
perform DMA transfers for small sizes of data (~16 to ~512 bytes),
resulting in cache issues in subsequent transfers. Increase the DMA
transfer threshold to 512 to allow the shorter transfers to take place,
bypassing DMA.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
 drivers/mmc/host/dw_mmc-exynos.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 261344d3a8cfe..4b76b997ddc15 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -141,6 +141,7 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
 		priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
 		/* Quirk needed for certain Exynos SoCs */
 		host->quirks |= DW_MMC_QUIRK_FIFO64_32;
+		host->dma_threshold = 512;
 	}
 
 	if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {

-- 
2.53.0


^ permalink raw reply related

* [PATCH v2 0/2] media: i2c: Add os02g10 camera sensor driver
From: Elgin Perumbilly @ 2026-04-14  8:49 UTC (permalink / raw)
  To: sakari.ailus, tarang.raval
  Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Vladimir Zapolskiy, Mehdi Djait, Laurent Pinchart,
	Sylvain Petinot, Benjamin Mugnier, Himanshu Bhavani,
	Jingjing Xiong, Svyatoslav Ryhel, Hardevsinh Palaniya,
	linux-media, devicetree, linux-kernel

The following features are supported:
- Manual exposure an gain control support.
- vblank/hblank control support.
- vflip/hflip control support
- Test pattern control support.
- Supported resolution: 1920 x 1080 @ 30fps (SBGGR10).

The driver is tested on mainline branch v7.0-rc2 on IMX8MP Debix Model a.

debix@imx8mp-debix:~$ v4l2-compliance -d /dev/v4l-subdev3
v4l2-compliance 1.31.0-5387, 64 bits, 64-bit time_t
v4l2-compliance SHA: 5508bc4301ac 2025-08-25 08:14:22

Compliance test for device /dev/v4l-subdev3:

Driver Info:
        Driver version   : 7.0.0
        Capabilities     : 0x00000000
        Client Capabilities: 0x0000000000000002
interval-uses-which
Required ioctls:
        test VIDIOC_SUDBEV_QUERYCAP: OK
        test invalid ioctls: OK

Allow for multiple opens:
        test second /dev/v4l-subdev3 open: OK
        test VIDIOC_SUBDEV_QUERYCAP: OK
        test for unlimited opens: OK

Debug ioctls:
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls:
        test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
        test VIDIOC_QUERYCTRL: OK
        test VIDIOC_G/S_CTRL: OK
        test VIDIOC_G/S/TRY_EXT_CTRLS: OK
        test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
        test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
        Standard Controls: 13 Private Controls: 0

Format ioctls:
        test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
        test VIDIOC_G/S_PARM: OK (Not Supported)
        test VIDIOC_G_FBUF: OK (Not Supported)
        test VIDIOC_G_FMT: OK (Not Supported)
        test VIDIOC_TRY_FMT: OK (Not Supported)
        test VIDIOC_S_FMT: OK (Not Supported)
        test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
        test Cropping: OK (Not Supported)
        test Composing: OK (Not Supported)
        test Scaling: OK (Not Supported)

Codec ioctls:
        test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
        test VIDIOC_G_ENC_INDEX: OK (Not Supported)
        test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

Buffer ioctls:
        test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
        test CREATE_BUFS maximum buffers: OK
        test VIDIOC_REMOVE_BUFS: OK
        test VIDIOC_EXPBUF: OK (Not Supported)
        test Requests: OK (Not Supported)
        test blocking wait: OK (Not Supported)

Total for device /dev/v4l-subdev3: 46, Succeeded: 46, Failed: 0, Warnings: 0


V1 -> V2
- Mode-specific register writes handled dynamically.
- Moved page 2 registers into a dedicated page 2 block.
- Used pm_runtime_get_if_active() in set_ctrls.
- Resolved negative hblank issue.
- Improved error messages in disable_stream.
- Updated V4L2_SEL_TGT_CROP and V4L2_SEL_TGT_CROP_BOUNDS.
- Added my name to the author list.

Elgin Perumbilly (2):
  dt-bindings: media: i2c: Add os02g10 sensor
  media: i2c: add os02g10 image sensor driver

 .../bindings/media/i2c/ovti,os02g10.yaml      |   96 ++
 MAINTAINERS                                   |    8 +
 drivers/media/i2c/Kconfig                     |   10 +
 drivers/media/i2c/Makefile                    |    1 +
 drivers/media/i2c/os02g10.c                   | 1039 +++++++++++++++++
 5 files changed, 1154 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
 create mode 100644 drivers/media/i2c/os02g10.c

--
2.34.1


^ permalink raw reply

* [PATCH v2 1/2] dt-bindings: media: i2c: Add os02g10 sensor
From: Elgin Perumbilly @ 2026-04-14  8:49 UTC (permalink / raw)
  To: sakari.ailus, tarang.raval
  Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Vladimir Zapolskiy, Mehdi Djait, Laurent Pinchart,
	Benjamin Mugnier, Sylvain Petinot, Hardevsinh Palaniya,
	Heimir Thor Sverrisson, Jingjing Xiong, Himanshu Bhavani,
	Svyatoslav Ryhel, linux-media, devicetree, linux-kernel
In-Reply-To: <20260414084952.217215-1-elgin.perumbilly@siliconsignals.io>

Add bindings for Omnivision OS02G10 sensor.

Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
---
 .../bindings/media/i2c/ovti,os02g10.yaml      | 96 +++++++++++++++++++
 MAINTAINERS                                   |  7 ++
 2 files changed, 103 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml

diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
new file mode 100644
index 000000000000..79e7e644d696
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,os02g10.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OS02G10 Image Sensor
+
+maintainers:
+  - Tarang Raval <tarang.raval@siliconsignals.io>
+
+description:
+  The OmniVision OS02G10 is a 2MP (1920x1080) color CMOS image sensor controlled
+  through an I2C-compatible SCCB bus. it outputs RAW10 format.
+
+properties:
+  compatible:
+    const: ovti,os02g10
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: XCLK clock
+
+  avdd-supply:
+    description: Analog Domain Power Supply (2.8v)
+
+  dovdd-supply:
+    description: I/O Domain Power Supply (1.8v)
+
+  dvdd-supply:
+    description: Digital core Power Supply (1.5v)
+
+  reset-gpios:
+    maxItems: 1
+    description: Reset Pin GPIO Control (active low)
+
+  port:
+    description: MIPI CSI-2 transmitter port
+    $ref: /schemas/graph.yaml#/$defs/port-base
+    additionalProperties: false
+
+    properties:
+      endpoint:
+        $ref: /schemas/media/video-interfaces.yaml#
+        unevaluatedProperties: false
+
+        properties:
+          data-lanes:
+            items:
+              - const: 1
+              - const: 2
+        required:
+          - data-lanes
+          - link-frequencies
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - avdd-supply
+  - dovdd-supply
+  - dvdd-supply
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        camera-sensor@3c {
+            compatible = "ovti,os02g10";
+            reg = <0x3c>;
+            clocks = <&os02g10_clk>;
+            reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+
+            avdd-supply = <&os02g10_avdd_2v8>;
+            dvdd-supply = <&os02g10_dvdd_1v2>;
+            dovdd-supply = <&os2gb10_dovdd_1v8>;
+
+            port {
+                cam_out: endpoint {
+                    remote-endpoint = <&mipi_in_cam>;
+                    data-lanes = <1 2>;
+                    link-frequencies = /bits/ 64 <720000000>;
+                };
+            };
+        };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..13409c71a765 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19457,6 +19457,13 @@ T:	git git://linuxtv.org/media_tree.git
 F:	Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
 F:	drivers/media/i2c/og0ve1b.c

+OMNIVISION OS02G10 SENSOR DRIVER
+M:	Tarang Raval <tarang.raval@siliconsignals.io>
+M:	Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
+
 OMNIVISION OS05B10 SENSOR DRIVER
 M:	Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
 M:	Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
--
2.34.1


^ permalink raw reply related

* [PATCH v2 2/2] media: i2c: add os02g10 image sensor driver
From: Elgin Perumbilly @ 2026-04-14  8:49 UTC (permalink / raw)
  To: sakari.ailus, tarang.raval
  Cc: Elgin Perumbilly, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Vladimir Zapolskiy, Mehdi Djait, Laurent Pinchart,
	Benjamin Mugnier, Sylvain Petinot, Hardevsinh Palaniya,
	Jingjing Xiong, linux-media, devicetree, linux-kernel
In-Reply-To: <20260414084952.217215-1-elgin.perumbilly@siliconsignals.io>

Add a v4l2 subdevice driver for the Omnivision os02g10 sensor.

The Omnivision os02g10 is a CMOS image sensor with an active array size of
1920 x 1080.

The following features are supported:
- Manual exposure an gain control support
- vblank/hblank control support
- vflip/hflip control support
- Test pattern control support
- Supported resolution: 1920 x 1080 @ 30fps (SBGGR10)

Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
Reviewed-by: Tarang Raval <tarang.raval@siliconsignals.io>
---
 MAINTAINERS                 |    1 +
 drivers/media/i2c/Kconfig   |   10 +
 drivers/media/i2c/Makefile  |    1 +
 drivers/media/i2c/os02g10.c | 1039 +++++++++++++++++++++++++++++++++++
 4 files changed, 1051 insertions(+)
 create mode 100644 drivers/media/i2c/os02g10.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 13409c71a765..28827e77ea31 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19463,6 +19463,7 @@ M:	Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
 L:	linux-media@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
+F:	drivers/media/i2c/os02g10.c

 OMNIVISION OS05B10 SENSOR DRIVER
 M:	Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5eb1e0e0a87a..dd6e9562acf6 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -372,6 +372,16 @@ config VIDEO_OG0VE1B
 	  To compile this driver as a module, choose M here: the
 	  module will be called og0ve1b.

+config VIDEO_OS02G10
+        tristate "OmniVision OS02G10 sensor support"
+        select V4L2_CCI_I2C
+        help
+          This is a Video4Linux2 sensor driver for Omnivision
+          OS02G10 camera sensor.
+
+	  To compile this driver as a module, choose M here: the
+          module will be called os02g10.
+
 config VIDEO_OS05B10
         tristate "OmniVision OS05B10 sensor support"
         select V4L2_CCI_I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a3a6396df3c4..a7554d2eb140 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
 obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
 obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o
+obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o
 obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o
 obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
 obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
diff --git a/drivers/media/i2c/os02g10.c b/drivers/media/i2c/os02g10.c
new file mode 100644
index 000000000000..b8df79162f88
--- /dev/null
+++ b/drivers/media/i2c/os02g10.c
@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Support for the OS02G10
+ *
+ * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
+ *
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/regmap.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+#define OS02G10_XCLK_FREQ			(24 * HZ_PER_MHZ)
+
+/* Add page number in CCI private bits [31:28] of the register address */
+#define OS02G10_PAGE_REG8(p, x)	 (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG8(x))
+#define OS02G10_PAGE_REG16(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG16(x))
+#define OS02G10_PAGE_REG24(p, x) (((p) << CCI_REG_PRIVATE_SHIFT) | CCI_REG24(x))
+
+#define OS02G10_REG_PAGE_SELECT			CCI_REG8(0xfd)
+
+/* Page 0 */
+#define OS02G10_REG_CHIPID			OS02G10_PAGE_REG24(0x00, 0x02)
+#define OS02G10_CHIPID				0x560247
+
+#define OS02G10_REG_PLL_DIV_CTRL		OS02G10_PAGE_REG8(0x00, 0x30)
+#define OS02G10_REG_PLL_DCTL_BIAS_CTRL		OS02G10_PAGE_REG8(0x00, 0x35)
+#define OS02G10_REG_GATE_EN_CTRL		OS02G10_PAGE_REG8(0x00, 0x38)
+#define OS02G10_REG_DPLL_NC			OS02G10_PAGE_REG8(0x00, 0x41)
+#define OS02G10_REG_MP_PHASE_CTRL		OS02G10_PAGE_REG8(0x00, 0x44)
+
+/* Page 1 */
+#define OS02G10_REG_STREAM_CTRL			OS02G10_PAGE_REG8(0x01, 0xb1)
+#define OS02G10_STREAM_CTRL_ON			0x03
+#define OS02G10_STREAM_CTRL_OFF			0x00
+
+#define OS02G10_REG_FRAME_SYNC			OS02G10_PAGE_REG8(0x01, 0x01)
+
+#define OS02G10_REG_FRAME_LENGTH		OS02G10_PAGE_REG16(0x01, 0x0e)
+#define OS02G10_FRAME_LENGTH_MAX		0xffff
+#define OS02G10_REG_HBLANK			OS02G10_PAGE_REG16(0x01, 0x09)
+
+#define OS02G10_REG_FRAME_TEST_CTRL		OS02G10_PAGE_REG8(0x01, 0x0d)
+#define OS02G10_FRAME_EXP_SEPERATE_EN		BIT(4)
+#define OS02G10_TEST_PATTERN_ENABLE		BIT(0)
+
+#define OS02G10_REG_ULP_PWD_DUMMY_CTRL		OS02G10_PAGE_REG8(0x01, 0x3c)
+#define OS02G10_REG_DC_LEVEL_LIMIT_EN		OS02G10_PAGE_REG8(0x01, 0x46)
+#define OS02G10_REG_DC_LEVEL_LIMIT_L		OS02G10_PAGE_REG8(0x01, 0x47)
+#define OS02G10_REG_BLC_DATA_LIMIT_L		OS02G10_PAGE_REG8(0x01, 0x48)
+#define OS02G10_REG_DC_BLC_LIMIT_H		OS02G10_PAGE_REG8(0x01, 0x49)
+
+#define OS02G10_REG_HS_LP_CTRL			OS02G10_PAGE_REG8(0x01, 0x92)
+#define OS02G10_REG_HS_LEVEL			OS02G10_PAGE_REG8(0x01, 0x9d)
+#define OS02G10_REG_HS_DRV			OS02G10_PAGE_REG8(0x01, 0x9e)
+
+#define OS02G10_REG_GB_SUBOFFSET		OS02G10_PAGE_REG8(0x01, 0xf0)
+#define OS02G10_REG_BLUE_SUBOFFSET		OS02G10_PAGE_REG8(0x01, 0xf1)
+#define OS02G10_REG_RED_SUBOFFSET		OS02G10_PAGE_REG8(0x01, 0xf2)
+#define OS02G10_REG_GR_SUBOFFSET		OS02G10_PAGE_REG8(0x01, 0xf3)
+
+#define OS02G10_REG_ABL_TRIGGER			OS02G10_PAGE_REG8(0x01, 0xfa)
+#define OS02G10_REG_ABL				OS02G10_PAGE_REG8(0x01, 0xfb)
+
+#define OS02G10_REG_H_SIZE_MIPI			OS02G10_PAGE_REG16(0x01, 0x8e)
+#define OS02G10_REG_V_SIZE_MIPI			OS02G10_PAGE_REG16(0x01, 0x90)
+#define OS02G10_REG_MIPI_TX_SPEED_CTRL		OS02G10_PAGE_REG8(0x01, 0xa1)
+
+#define OS02G10_REG_LONG_EXPOSURE		OS02G10_PAGE_REG16(0x01, 0x03)
+#define OS02G10_EXPOSURE_MIN			4
+#define OS02G10_EXPOSURE_STEP			1
+#define OS02G10_EXPOSURE_MARGIN			9
+
+#define OS02G10_REG_ANALOG_GAIN			OS02G10_PAGE_REG8(0x01, 0x24)
+#define OS02G10_ANALOG_GAIN_MIN			0x10
+#define OS02G10_ANALOG_GAIN_MAX			0xf8
+#define OS02G10_ANALOG_GAIN_STEP		1
+#define OS02G10_ANALOG_GAIN_DEFAULT		0x10
+
+#define OS02G10_REG_DIGITAL_GAIN_H		OS02G10_PAGE_REG8(0x01, 0x37)
+#define OS02G10_REG_DIGITAL_GAIN_L		OS02G10_PAGE_REG8(0x01, 0x39)
+#define OS02G10_DIGITAL_GAIN_MIN		0x40
+#define OS02G10_DIGITAL_GAIN_MAX		0x800
+#define OS02G10_DIGITAL_GAIN_STEP		64
+#define OS02G10_DIGITAL_GAIN_DEFAULT		0x40
+
+#define OS02G10_REG_FLIP_MIRROR			OS02G10_PAGE_REG8(0x01, 0x3f)
+#define OS02G10_FLIP				BIT(1)
+#define OS02G10_MIRROR				BIT(0)
+
+/* Page 2 */
+#define OS02G10_REG_V_START			OS02G10_PAGE_REG16(0x02, 0xa0)
+#define OS02G10_REG_V_SIZE			OS02G10_PAGE_REG16(0x02, 0xa2)
+#define OS02G10_REG_H_START			OS02G10_PAGE_REG16(0x02, 0xa4)
+#define OS02G10_REG_H_SIZE			OS02G10_PAGE_REG16(0x02, 0xa6)
+
+#define OS02G10_REG_SIF_CTRL			OS02G10_PAGE_REG8(0x02, 0x5e)
+#define OS02G10_ORIENTATION_BAYER_FIX		0x32
+
+#define OS02G10_LINK_FREQ_720MHZ		(720 * HZ_PER_MHZ)
+
+/* OS02G10 native and active pixel array size */
+static const struct v4l2_rect os02g10_native_area = {
+	.top = 0,
+	.left = 0,
+	.width = 1928,
+	.height = 1088,
+};
+
+static const struct v4l2_rect os02g10_active_area = {
+	.top = 4,
+	.left = 4,
+	.width = 1920,
+	.height = 1080,
+};
+
+static const char * const os02g10_supply_name[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+struct os02g10 {
+	struct device *dev;
+	struct regmap *cci;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct clk *xclk;
+	struct gpio_desc *reset_gpio;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(os02g10_supply_name)];
+
+	/* V4L2 Controls */
+	struct v4l2_ctrl_handler handler;
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *vflip;
+	struct v4l2_ctrl *hflip;
+
+	u32 link_freq_index;
+
+	u8 current_page;
+	struct mutex page_lock;
+};
+
+struct os02g10_mode {
+	u32 width;
+	u32 height;
+	u32 vts_def;
+	u32 hts_def;
+	u32 exp_def;
+	u32 x_start;
+	u32 y_start;
+};
+
+static const struct cci_reg_sequence os02g10_common_regs[] = {
+	{ OS02G10_REG_PLL_DIV_CTRL,		0x0a},
+	{ OS02G10_REG_PLL_DCTL_BIAS_CTRL,	0x04},
+	{ OS02G10_REG_GATE_EN_CTRL,		0x11},
+	{ OS02G10_REG_DPLL_NC,			0x06},
+	{ OS02G10_REG_MP_PHASE_CTRL,		0x20},
+	{ OS02G10_PAGE_REG8(0x01, 0x19),	0x50},
+	{ OS02G10_PAGE_REG8(0x01, 0x1a),	0x0c},
+	{ OS02G10_PAGE_REG8(0x01, 0x1b),	0x0d},
+	{ OS02G10_PAGE_REG8(0x01, 0x1c),	0x00},
+	{ OS02G10_PAGE_REG8(0x01, 0x1d),	0x75},
+	{ OS02G10_PAGE_REG8(0x01, 0x1e),	0x52},
+	{ OS02G10_PAGE_REG8(0x01, 0x22),	0x14},
+	{ OS02G10_PAGE_REG8(0x01, 0x25),	0x44},
+	{ OS02G10_PAGE_REG8(0x01, 0x26),	0x0f},
+	{ OS02G10_REG_ULP_PWD_DUMMY_CTRL,	0xca},
+	{ OS02G10_PAGE_REG8(0x01, 0x3d),	0x4a},
+	{ OS02G10_PAGE_REG8(0x01, 0x40),	0x0f},
+	{ OS02G10_PAGE_REG8(0x01, 0x43),	0x38},
+	{ OS02G10_REG_DC_LEVEL_LIMIT_EN,	0x01},
+	{ OS02G10_REG_DC_LEVEL_LIMIT_L,		0x00},
+	{ OS02G10_REG_DC_BLC_LIMIT_H,		0x32},
+	{ OS02G10_PAGE_REG8(0x01, 0x50),	0x01},
+	{ OS02G10_PAGE_REG8(0x01, 0x51),	0x28},
+	{ OS02G10_PAGE_REG8(0x01, 0x52),	0x20},
+	{ OS02G10_PAGE_REG8(0x01, 0x53),	0x03},
+	{ OS02G10_PAGE_REG8(0x01, 0x57),	0x16},
+	{ OS02G10_PAGE_REG8(0x01, 0x59),	0x01},
+	{ OS02G10_PAGE_REG8(0x01, 0x5a),	0x01},
+	{ OS02G10_PAGE_REG8(0x01, 0x5d),	0x04},
+	{ OS02G10_PAGE_REG8(0x01, 0x6a),	0x04},
+	{ OS02G10_PAGE_REG8(0x01, 0x6b),	0x03},
+	{ OS02G10_PAGE_REG8(0x01, 0x6e),	0x28},
+	{ OS02G10_PAGE_REG8(0x01, 0x71),	0xc2},
+	{ OS02G10_PAGE_REG8(0x01, 0x72),	0x04},
+	{ OS02G10_PAGE_REG8(0x01, 0x73),	0x38},
+	{ OS02G10_PAGE_REG8(0x01, 0x74),	0x04},
+	{ OS02G10_PAGE_REG8(0x01, 0x79),	0x00},
+	{ OS02G10_PAGE_REG8(0x01, 0x7a),	0xb2},
+	{ OS02G10_PAGE_REG8(0x01, 0x7b),	0x10},
+	{ OS02G10_REG_HS_LP_CTRL,		0x02},
+	{ OS02G10_REG_HS_LEVEL,			0x03},
+	{ OS02G10_REG_HS_DRV,			0x55},
+	{ OS02G10_PAGE_REG8(0x01, 0xb8),	0x70},
+	{ OS02G10_PAGE_REG8(0x01, 0xb9),	0x70},
+	{ OS02G10_PAGE_REG8(0x01, 0xba),	0x70},
+	{ OS02G10_PAGE_REG8(0x01, 0xbb),	0x70},
+	{ OS02G10_PAGE_REG8(0x01, 0xbc),	0x00},
+	{ OS02G10_PAGE_REG8(0x01, 0xc4),	0x6d},
+	{ OS02G10_PAGE_REG8(0x01, 0xc5),	0x6d},
+	{ OS02G10_PAGE_REG8(0x01, 0xc6),	0x6d},
+	{ OS02G10_PAGE_REG8(0x01, 0xc7),	0x6d},
+	{ OS02G10_PAGE_REG8(0x01, 0xcc),	0x11},
+	{ OS02G10_PAGE_REG8(0x01, 0xcd),	0xe0},
+	{ OS02G10_PAGE_REG8(0x01, 0xd0),	0x1b},
+	{ OS02G10_PAGE_REG8(0x01, 0xd2),	0x76},
+	{ OS02G10_PAGE_REG8(0x01, 0xd3),	0x68},
+	{ OS02G10_PAGE_REG8(0x01, 0xd4),	0x68},
+	{ OS02G10_PAGE_REG8(0x01, 0xd5),	0x73},
+	{ OS02G10_PAGE_REG8(0x01, 0xd6),	0x73},
+	{ OS02G10_PAGE_REG8(0x01, 0xe8),	0x55},
+	{ OS02G10_REG_GB_SUBOFFSET,		0x40},
+	{ OS02G10_REG_BLUE_SUBOFFSET,		0x40},
+	{ OS02G10_REG_RED_SUBOFFSET,		0x40},
+	{ OS02G10_REG_GR_SUBOFFSET,		0x40},
+	{ OS02G10_REG_ABL_TRIGGER,		0x1c},
+	{ OS02G10_REG_ABL,			0x33},
+	{ OS02G10_PAGE_REG8(0x01, 0xfc),	0x80},
+	{ OS02G10_PAGE_REG8(0x01, 0xfe),	0x80},
+	{ OS02G10_PAGE_REG8(0x03, 0x03),	0x67},
+	{ OS02G10_PAGE_REG8(0x03, 0x00),	0x59},
+	{ OS02G10_PAGE_REG8(0x03, 0x04),	0x11},
+	{ OS02G10_PAGE_REG8(0x03, 0x05),	0x04},
+	{ OS02G10_PAGE_REG8(0x03, 0x06),	0x0c},
+	{ OS02G10_PAGE_REG8(0x03, 0x07),	0x08},
+	{ OS02G10_PAGE_REG8(0x03, 0x08),	0x08},
+	{ OS02G10_PAGE_REG8(0x03, 0x09),	0x4f},
+	{ OS02G10_PAGE_REG8(0x03, 0x0b),	0x08},
+	{ OS02G10_PAGE_REG8(0x03, 0x0d),	0x26},
+	{ OS02G10_PAGE_REG8(0x03, 0x0f),	0x00},
+	{ OS02G10_PAGE_REG8(0x02, 0x34),	0xfe},
+	{ OS02G10_REG_MIPI_TX_SPEED_CTRL,	0x05},
+};
+
+static const struct os02g10_mode supported_modes[] = {
+	{
+		.width = 1920,
+		.height = 1080,
+		.vts_def = 1246,
+		.hts_def = 1082,
+		.exp_def = 1100,
+		.x_start = 2,
+		.y_start = 6,
+	},
+};
+
+static const s64 link_freq_menu_items[] = {
+	OS02G10_LINK_FREQ_720MHZ,
+};
+
+static const char * const os02g10_test_pattern_menu[] = {
+	"Disabled",
+	"Colorbar",
+};
+
+static int os02g10_page_access(struct os02g10 *os02g10, u32 reg, int *err)
+{
+	u8 page = (reg & CCI_REG_PRIVATE_MASK) >> CCI_REG_PRIVATE_SHIFT;
+	int ret = 0;
+
+	if (err && *err)
+		return *err;
+
+	guard(mutex)(&os02g10->page_lock);
+
+	/* Perform page access before read/write */
+	if (os02g10->current_page == page)
+		return ret;
+
+	ret = cci_write(os02g10->cci, OS02G10_REG_PAGE_SELECT, page, err);
+	if (!ret)
+		os02g10->current_page = page;
+
+	return ret;
+}
+
+static int os02g10_read(struct os02g10 *os02g10, u32 reg, u64 *val, int *err)
+{
+	u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+	int ret;
+
+	ret = os02g10_page_access(os02g10, reg, err);
+	if (ret)
+		return ret;
+
+	return cci_read(os02g10->cci, addr, val, err);
+}
+
+static int os02g10_write(struct os02g10 *os02g10, u32 reg, u64 val, int *err)
+{
+	u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+	int ret;
+
+	ret = os02g10_page_access(os02g10, reg, err);
+	if (ret)
+		return ret;
+
+	return cci_write(os02g10->cci, addr, val, err);
+}
+
+static int os02g10_update_bits(struct os02g10 *os02g10, u32 reg, u64 mask,
+			       u64 val, int *err)
+{
+	u32 addr = reg & ~CCI_REG_PRIVATE_MASK;
+	int ret;
+
+	ret = os02g10_page_access(os02g10, reg, err);
+	if (ret)
+		return ret;
+
+	return cci_update_bits(os02g10->cci, addr, mask, val, err);
+}
+
+static int os02g10_multi_reg_write(struct os02g10 *os02g10,
+				   const struct cci_reg_sequence *regs,
+				   unsigned int num_regs, int *err)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < num_regs; i++) {
+		ret = os02g10_write(os02g10, regs[i].reg, regs[i].val, err);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static inline struct os02g10 *to_os02g10(struct v4l2_subdev *_sd)
+{
+	return container_of_const(_sd, struct os02g10, sd);
+}
+
+static u32 os02g10_get_format_code(struct os02g10 *os02g10)
+{
+	static const u32 codes[2][2] = {
+		{ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
+		{ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
+	};
+
+	u32 code = codes[os02g10->vflip->val][os02g10->hflip->val];
+
+	return code;
+}
+
+static int os02g10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct os02g10 *os02g10 = container_of_const(ctrl->handler,
+						     struct os02g10, handler);
+	struct v4l2_subdev_state *state;
+	struct v4l2_mbus_framefmt *fmt;
+	int ret = 0;
+
+	state = v4l2_subdev_get_locked_active_state(&os02g10->sd);
+	fmt = v4l2_subdev_state_get_format(state, 0);
+
+	if (ctrl->id == V4L2_CID_VBLANK) {
+		/* Honour the VBLANK limits when setting exposure */
+		s64 max = fmt->height + ctrl->val - OS02G10_EXPOSURE_MARGIN;
+
+		ret = __v4l2_ctrl_modify_range(os02g10->exposure,
+					       os02g10->exposure->minimum, max,
+					       os02g10->exposure->step,
+					       os02g10->exposure->default_value);
+		if (ret)
+			return ret;
+	}
+
+	if (pm_runtime_get_if_active(os02g10->dev) == 0)
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		os02g10_write(os02g10, OS02G10_REG_LONG_EXPOSURE, ctrl->val, &ret);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		os02g10_write(os02g10, OS02G10_REG_ANALOG_GAIN, ctrl->val, &ret);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		os02g10_write(os02g10, OS02G10_REG_DIGITAL_GAIN_L,
+			      (ctrl->val & 0xff), &ret);
+		os02g10_write(os02g10, OS02G10_REG_DIGITAL_GAIN_H,
+			      ((ctrl->val >> 8) & 0x7), &ret);
+		break;
+	case V4L2_CID_VBLANK:
+		u64 vts = ctrl->val + fmt->height;
+
+		os02g10_update_bits(os02g10, OS02G10_REG_FRAME_TEST_CTRL,
+				    OS02G10_FRAME_EXP_SEPERATE_EN,
+				    OS02G10_FRAME_EXP_SEPERATE_EN, &ret);
+		os02g10_write(os02g10, OS02G10_REG_FRAME_LENGTH, vts, &ret);
+		break;
+	case V4L2_CID_HFLIP:
+	case V4L2_CID_VFLIP:
+		os02g10_write(os02g10, OS02G10_REG_FLIP_MIRROR,
+			      os02g10->hflip->val | os02g10->vflip->val << 1,
+			      &ret);
+		os02g10_write(os02g10, OS02G10_REG_SIF_CTRL,
+			      OS02G10_ORIENTATION_BAYER_FIX, &ret);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		os02g10_update_bits(os02g10,
+				    OS02G10_REG_FRAME_TEST_CTRL,
+				    OS02G10_TEST_PATTERN_ENABLE,
+				    ctrl->val ? OS02G10_TEST_PATTERN_ENABLE : 0,
+				    &ret);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	os02g10_write(os02g10, OS02G10_REG_FRAME_SYNC, 0x01, &ret);
+
+	pm_runtime_put(os02g10->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops os02g10_ctrl_ops = {
+	.s_ctrl = os02g10_set_ctrl,
+};
+
+static int os02g10_init_controls(struct os02g10 *os02g10)
+{
+	const struct os02g10_mode *mode = &supported_modes[0];
+	u64 vblank_def, hblank_def, exp_max, pixel_rate;
+	struct v4l2_fwnode_device_properties props;
+	struct v4l2_ctrl_handler *ctrl_hdlr;
+	int ret;
+
+	ctrl_hdlr = &os02g10->handler;
+	v4l2_ctrl_handler_init(ctrl_hdlr, 12);
+
+	/* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+	pixel_rate = div_u64(OS02G10_LINK_FREQ_720MHZ * 2 * 2, 10);
+	v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0,
+			  pixel_rate, 1, pixel_rate);
+
+	os02g10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os02g10_ctrl_ops,
+						    V4L2_CID_LINK_FREQ,
+						    os02g10->link_freq_index,
+						    0, link_freq_menu_items);
+	if (os02g10->link_freq)
+		os02g10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	/*
+	 * Multiply by 2 to ensure positive hblank.
+	 * Datasheet does not provide information about the unit of HTS.
+	 */
+	hblank_def = (mode->hts_def * 2) - mode->width;
+	os02g10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+					    V4L2_CID_HBLANK, hblank_def, hblank_def,
+					    1, hblank_def);
+	if (os02g10->hblank)
+		os02g10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	vblank_def = mode->vts_def - mode->height;
+	os02g10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_def,
+					    OS02G10_FRAME_LENGTH_MAX - mode->height,
+					    1, vblank_def);
+
+	exp_max = mode->vts_def - OS02G10_EXPOSURE_MARGIN;
+	os02g10->exposure =
+		v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+				  V4L2_CID_EXPOSURE,
+				  OS02G10_EXPOSURE_MIN, exp_max,
+				  OS02G10_EXPOSURE_STEP, mode->exp_def);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+			  V4L2_CID_ANALOGUE_GAIN, OS02G10_ANALOG_GAIN_MIN,
+			  OS02G10_ANALOG_GAIN_MAX, OS02G10_ANALOG_GAIN_STEP,
+			  OS02G10_ANALOG_GAIN_DEFAULT);
+
+	v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+			  V4L2_CID_DIGITAL_GAIN, OS02G10_DIGITAL_GAIN_MIN,
+			  OS02G10_DIGITAL_GAIN_MAX, OS02G10_DIGITAL_GAIN_STEP,
+			  OS02G10_DIGITAL_GAIN_DEFAULT);
+
+	os02g10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+					   V4L2_CID_HFLIP, 0, 1, 1, 0);
+	if (os02g10->hflip)
+		os02g10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	os02g10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+					   V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (os02g10->vflip)
+		os02g10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &os02g10_ctrl_ops,
+				     V4L2_CID_TEST_PATTERN,
+				     ARRAY_SIZE(os02g10_test_pattern_menu) - 1,
+				     0, 0, os02g10_test_pattern_menu);
+	if (ctrl_hdlr->error) {
+		ret = ctrl_hdlr->error;
+		dev_err(os02g10->dev, "control init failed (%d)\n", ret);
+		goto err_handler_free;
+	}
+
+	ret = v4l2_fwnode_device_parse(os02g10->dev, &props);
+	if (ret)
+		goto err_handler_free;
+
+	ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr,
+					      &os02g10_ctrl_ops, &props);
+	if (ret)
+		goto err_handler_free;
+
+	os02g10->sd.ctrl_handler = ctrl_hdlr;
+
+	return 0;
+
+err_handler_free:
+	v4l2_ctrl_handler_free(ctrl_hdlr);
+
+	return ret;
+}
+
+static int os02g10_set_framefmt(struct os02g10 *os02g10,
+				struct v4l2_subdev_state *state)
+{
+	const struct v4l2_mbus_framefmt *format;
+	const struct os02g10_mode *mode;
+	int ret = 0;
+
+	format = v4l2_subdev_state_get_format(state, 0);
+	mode = v4l2_find_nearest_size(supported_modes,
+				      ARRAY_SIZE(supported_modes), width,
+				      height, format->width, format->height);
+
+	os02g10_write(os02g10, OS02G10_REG_V_START, mode->y_start, &ret);
+	os02g10_write(os02g10, OS02G10_REG_V_SIZE, mode->height, &ret);
+	os02g10_write(os02g10, OS02G10_REG_V_SIZE_MIPI, mode->height, &ret);
+	os02g10_write(os02g10, OS02G10_REG_H_START, mode->x_start, &ret);
+	os02g10_write(os02g10, OS02G10_REG_H_SIZE, mode->width, &ret);
+	os02g10_write(os02g10, OS02G10_REG_H_SIZE_MIPI, mode->width, &ret);
+
+	return ret;
+}
+
+static int os02g10_enable_streams(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *state, u32 pad,
+				  u64 streams_mask)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(os02g10->dev);
+	if (ret < 0)
+		return ret;
+
+	ret = os02g10_multi_reg_write(os02g10, os02g10_common_regs,
+				      ARRAY_SIZE(os02g10_common_regs), NULL);
+	if (ret) {
+		dev_err(os02g10->dev, "failed to write common registers\n");
+		goto err_rpm_put;
+	}
+
+	ret = os02g10_set_framefmt(os02g10, state);
+	if (ret) {
+		dev_err(os02g10->dev, "failed to set frame foramt\n");
+		goto err_rpm_put;
+	}
+
+	/* Apply customized values from user */
+	ret = __v4l2_ctrl_handler_setup(os02g10->sd.ctrl_handler);
+	if (ret)
+		goto err_rpm_put;
+
+	ret = os02g10_write(os02g10, OS02G10_REG_STREAM_CTRL,
+			    OS02G10_STREAM_CTRL_ON, NULL);
+	if (ret)
+		goto err_rpm_put;
+
+	/* vflip and hflip cannot change during streaming */
+	__v4l2_ctrl_grab(os02g10->vflip, true);
+	__v4l2_ctrl_grab(os02g10->hflip, true);
+
+	return 0;
+
+err_rpm_put:
+	pm_runtime_put(os02g10->dev);
+	return ret;
+}
+
+static int os02g10_disable_streams(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *state, u32 pad,
+				   u64 streams_mask)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+	int ret;
+
+	ret = os02g10_write(os02g10, OS02G10_REG_STREAM_CTRL,
+			    OS02G10_STREAM_CTRL_OFF, NULL);
+	if (ret)
+		dev_err(os02g10->dev, "Failed to stop stream\n");
+
+	__v4l2_ctrl_grab(os02g10->vflip, false);
+	__v4l2_ctrl_grab(os02g10->hflip, false);
+
+	pm_runtime_put(os02g10->dev);
+
+	return ret;
+}
+
+static int os02g10_get_selection(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_state *sd_state,
+				 struct v4l2_subdev_selection *sel)
+{
+	switch (sel->target) {
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_NATIVE_SIZE:
+		sel->r = os02g10_native_area;
+		return 0;
+	case V4L2_SEL_TGT_CROP:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		sel->r = os02g10_active_area;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int os02g10_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+
+	if (code->index)
+		return -EINVAL;
+
+	code->code = os02g10_get_format_code(os02g10);
+
+	return 0;
+}
+
+static int os02g10_enum_frame_size(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_state *sd_state,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != os02g10_get_format_code(os02g10))
+		return -EINVAL;
+
+	fse->min_width = supported_modes[fse->index].width;
+	fse->max_width = fse->min_width;
+	fse->min_height = supported_modes[fse->index].height;
+	fse->max_height = fse->min_height;
+
+	return 0;
+}
+
+static int os02g10_set_pad_format(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_state *sd_state,
+				  struct v4l2_subdev_format *fmt)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+	struct v4l2_mbus_framefmt *format;
+	const struct os02g10_mode *mode;
+	int ret;
+
+	format = v4l2_subdev_state_get_format(sd_state, 0);
+
+	mode = v4l2_find_nearest_size(supported_modes,
+				      ARRAY_SIZE(supported_modes),
+				      width, height,
+				      fmt->format.width, fmt->format.height);
+
+	fmt->format.code = os02g10_get_format_code(os02g10);
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+	fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+	fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+	*format = fmt->format;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		u32 vblank_def = mode->vts_def - mode->height;
+
+		ret = __v4l2_ctrl_modify_range(os02g10->vblank, vblank_def,
+					       OS02G10_FRAME_LENGTH_MAX -
+					       mode->height, 1, vblank_def);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int os02g10_init_state(struct v4l2_subdev *sd,
+			      struct v4l2_subdev_state *state)
+{
+	struct os02g10 *os02g10 = to_os02g10(sd);
+	struct v4l2_subdev_format fmt = {
+		.which = V4L2_SUBDEV_FORMAT_TRY,
+		.format = {
+			.code = os02g10_get_format_code(os02g10),
+			.width = supported_modes[0].width,
+			.height = supported_modes[0].height,
+		},
+	};
+
+	os02g10_set_pad_format(sd, state, &fmt);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_video_ops os02g10_video_ops = {
+	.s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops os02g10_pad_ops = {
+	.enum_mbus_code = os02g10_enum_mbus_code,
+	.get_fmt = v4l2_subdev_get_fmt,
+	.set_fmt = os02g10_set_pad_format,
+	.get_selection = os02g10_get_selection,
+	.enum_frame_size = os02g10_enum_frame_size,
+	.enable_streams = os02g10_enable_streams,
+	.disable_streams = os02g10_disable_streams,
+};
+
+static const struct v4l2_subdev_ops os02g10_subdev_ops = {
+	.video = &os02g10_video_ops,
+	.pad = &os02g10_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops os02g10_internal_ops = {
+	.init_state = os02g10_init_state,
+};
+
+static int os02g10_power_on(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct os02g10 *os02g10 = to_os02g10(sd);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(os02g10_supply_name),
+				    os02g10->supplies);
+	if (ret) {
+		dev_err(os02g10->dev, "failed to enable regulators\n");
+		return ret;
+	}
+
+	/* T4: delay from DOVDD stable to MCLK on */
+	fsleep(5 * USEC_PER_MSEC);
+
+	ret = clk_prepare_enable(os02g10->xclk);
+	if (ret) {
+		dev_err(os02g10->dev, "failed to enable clock\n");
+		goto err_regulator_off;
+	}
+
+	/* T3: delay from DVDD stable to sensor power up stable */
+	fsleep(5 * USEC_PER_MSEC);
+
+	gpiod_set_value_cansleep(os02g10->reset_gpio, 0);
+
+	/* T5: delay from sensor power up stable to SCCB initialization */
+	fsleep(5 * USEC_PER_MSEC);
+
+	return 0;
+
+err_regulator_off:
+	regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+	return ret;
+}
+
+static int os02g10_power_off(struct device *dev)
+{
+	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+	struct os02g10 *os02g10 = to_os02g10(sd);
+
+	clk_disable_unprepare(os02g10->xclk);
+	gpiod_set_value_cansleep(os02g10->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+
+	return 0;
+}
+
+static int os02g10_identify_module(struct os02g10 *os02g10)
+{
+	u64 chip_id;
+	int ret;
+
+	ret = os02g10_read(os02g10, OS02G10_REG_CHIPID, &chip_id, NULL);
+	if (ret)
+		return dev_err_probe(os02g10->dev, ret,
+				     "failed to read chip id %x\n",
+				     OS02G10_CHIPID);
+
+	if (chip_id != OS02G10_CHIPID)
+		return dev_err_probe(os02g10->dev, -EIO,
+				     "chip id mismatch: %x!=%llx\n",
+				     OS02G10_CHIPID, chip_id);
+
+	return 0;
+}
+
+static int os02g10_parse_endpoint(struct os02g10 *os02g10)
+{
+	struct v4l2_fwnode_endpoint bus_cfg = {
+		.bus_type = V4L2_MBUS_CSI2_DPHY,
+	};
+	unsigned long link_freq_bitmap;
+	struct fwnode_handle *ep;
+	int ret;
+
+	ep = fwnode_graph_get_next_endpoint(dev_fwnode(os02g10->dev), NULL);
+	if (!ep)
+		return dev_err_probe(os02g10->dev, -ENXIO,
+				     "Failed to get next endpoint\n");
+
+	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+	fwnode_handle_put(ep);
+	if (ret)
+		return ret;
+
+	if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) {
+		ret = dev_err_probe(os02g10->dev, -EINVAL,
+				    "only 2 data lanes are supported\n");
+		goto error_out;
+	}
+
+	ret = v4l2_link_freq_to_bitmap(os02g10->dev, bus_cfg.link_frequencies,
+				       bus_cfg.nr_of_link_frequencies,
+				       link_freq_menu_items,
+				       ARRAY_SIZE(link_freq_menu_items),
+				       &link_freq_bitmap);
+	if (ret) {
+		ret = dev_err_probe(os02g10->dev, -EINVAL,
+				    "only 720MHz frequency is available\n");
+		goto error_out;
+	}
+
+	os02g10->link_freq_index = __ffs(link_freq_bitmap);
+
+error_out:
+	v4l2_fwnode_endpoint_free(&bus_cfg);
+
+	return ret;
+};
+
+static int os02g10_probe(struct i2c_client *client)
+{
+	struct os02g10 *os02g10;
+	unsigned int xclk_freq;
+	int ret;
+
+	os02g10 = devm_kzalloc(&client->dev, sizeof(*os02g10), GFP_KERNEL);
+	if (!os02g10)
+		return -ENOMEM;
+
+	os02g10->dev = &client->dev;
+
+	v4l2_i2c_subdev_init(&os02g10->sd, client, &os02g10_subdev_ops);
+	os02g10->sd.internal_ops = &os02g10_internal_ops;
+
+	os02g10->cci = devm_cci_regmap_init_i2c(client, 8);
+	if (IS_ERR(os02g10->cci))
+		return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->cci),
+				     "failed to initialize CCI\n");
+
+	/* Set Current page to 0 */
+	os02g10->current_page = 0;
+
+	ret = devm_mutex_init(os02g10->dev, &os02g10->page_lock);
+	if (ret)
+		return dev_err_probe(os02g10->dev, ret,
+				     "Failed to initialize lock\n");
+
+	/* Get system clock (xvclk) */
+	os02g10->xclk = devm_v4l2_sensor_clk_get(os02g10->dev, NULL);
+	if (IS_ERR(os02g10->xclk))
+		return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->xclk),
+				     "failed to get xclk\n");
+
+	xclk_freq = clk_get_rate(os02g10->xclk);
+	if (xclk_freq != OS02G10_XCLK_FREQ)
+		return dev_err_probe(os02g10->dev, -EINVAL,
+				     "xclk frequency not supported: %u Hz\n",
+				     xclk_freq);
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(os02g10_supply_name); i++)
+		os02g10->supplies[i].supply = os02g10_supply_name[i];
+
+	ret = devm_regulator_bulk_get(os02g10->dev,
+				      ARRAY_SIZE(os02g10_supply_name),
+				      os02g10->supplies);
+	if (ret)
+		return dev_err_probe(os02g10->dev, ret,
+				     "failed to get regulators\n");
+
+	ret = os02g10_parse_endpoint(os02g10);
+	if (ret)
+		return dev_err_probe(os02g10->dev, ret,
+				     "failed to parse endpoint configuration\n");
+
+	os02g10->reset_gpio = devm_gpiod_get_optional(os02g10->dev,
+						      "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(os02g10->reset_gpio))
+		return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->reset_gpio),
+				     "failed to get reset GPIO\n");
+
+	ret = os02g10_power_on(os02g10->dev);
+	if (ret)
+		return ret;
+
+	ret = os02g10_identify_module(os02g10);
+	if (ret)
+		goto error_power_off;
+
+	ret = os02g10_init_controls(os02g10);
+	if (ret)
+		goto error_power_off;
+
+	/* Initialize subdev */
+	os02g10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	os02g10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	os02g10->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+	ret = media_entity_pads_init(&os02g10->sd.entity, 1, &os02g10->pad);
+	if (ret) {
+		dev_err_probe(os02g10->dev, ret, "failed to init entity pads\n");
+		goto error_handler_free;
+	}
+
+	os02g10->sd.state_lock = os02g10->handler.lock;
+	ret = v4l2_subdev_init_finalize(&os02g10->sd);
+	if (ret) {
+		dev_err_probe(os02g10->dev, ret, "subdev init error\n");
+		goto error_media_entity;
+	}
+
+	pm_runtime_set_active(os02g10->dev);
+	pm_runtime_enable(os02g10->dev);
+
+	ret = v4l2_async_register_subdev_sensor(&os02g10->sd);
+	if (ret) {
+		dev_err_probe(os02g10->dev, ret,
+			      "failed to register os02g10 sub-device\n");
+		goto error_subdev_cleanup;
+	}
+
+	pm_runtime_idle(os02g10->dev);
+	return 0;
+
+error_subdev_cleanup:
+	v4l2_subdev_cleanup(&os02g10->sd);
+	pm_runtime_disable(os02g10->dev);
+	pm_runtime_set_suspended(os02g10->dev);
+
+error_media_entity:
+	media_entity_cleanup(&os02g10->sd.entity);
+
+error_handler_free:
+	v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+error_power_off:
+	os02g10_power_off(os02g10->dev);
+
+	return ret;
+}
+
+static void os02g10_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct os02g10 *os02g10 = to_os02g10(sd);
+
+	v4l2_async_unregister_subdev(sd);
+	v4l2_subdev_cleanup(&os02g10->sd);
+	media_entity_cleanup(&sd->entity);
+	v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev)) {
+		os02g10_power_off(&client->dev);
+		pm_runtime_set_suspended(&client->dev);
+	}
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(os02g10_pm_ops,
+				 os02g10_power_off, os02g10_power_on, NULL);
+
+static const struct of_device_id os02g10_id[] = {
+	{ .compatible = "ovti,os02g10" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, os02g10_id);
+
+static struct i2c_driver os02g10_driver = {
+	.driver = {
+		.name = "os02g10",
+		.pm = pm_ptr(&os02g10_pm_ops),
+		.of_match_table = os02g10_id,
+	},
+	.probe = os02g10_probe,
+	.remove = os02g10_remove,
+};
+module_i2c_driver(os02g10_driver);
+
+MODULE_DESCRIPTION("OS02G10 Camera Sensor Driver");
+MODULE_AUTHOR("Tarang Raval <tarang.raval@siliconsignals.io>");
+MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>");
+MODULE_LICENSE("GPL");
--
2.34.1


^ permalink raw reply related

* Re: [PATCH 2/2] pwm: pxa: Add optional bus clock
From: Uwe Kleine-König @ 2026-04-14  8:51 UTC (permalink / raw)
  To: Yixun Lan
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Duje Mihanović, linux-pwm, devicetree, linux-kernel,
	linux-riscv, spacemit
In-Reply-To: <20260414060453-GKA203300@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 793 bytes --]

Hello,

On Tue, Apr 14, 2026 at 06:04:53AM +0000, Yixun Lan wrote:
> On 10:38 Mon 13 Apr     , Uwe Kleine-König wrote:
> > On Thu, Apr 09, 2026 at 12:45:12AM +0000, Yixun Lan wrote:
> > > +	/* Get named func clk if bus clock is valid */
> > > +	pc->clk = devm_clk_get(dev, pc->bus_clk ? "func" : NULL);
> > 
> > I'm not sure, but I think passing "func" unconditionally to
> > devm_clk_get() would also work fine.
> Passing "func" unconditionally, will break old compatibles(not k3-pwm), as only
> one clocks property is provided, but no clock-names property

I thought that if there is a single clock without a name,
clk_get(dev, "somename") will return that one. But looking at the code,
that assumption is wrong. So keeping the conditional here is fine.

Best regards
Uwe

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH] arm64: dts: exynos850: Add SRAM node
From: Alexey Klimov @ 2026-04-14  9:00 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Alexey Klimov, Sam Protsenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Alim Akhtar
  Cc: linux-samsung-soc, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <2ff077e1-8983-4a41-bb21-5e4140545aa3@kernel.org>

On Mon Apr 13, 2026 at 4:23 PM BST, Krzysztof Kozlowski wrote:
> On 13/04/2026 16:52, Alexey Klimov wrote:
>> SRAM is used by the ACPM protocol to retrieve the ACPM channels
>> information and configuration data. Add the SRAM node.
>> 
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>> ---
>>  arch/arm64/boot/dts/exynos/exynos850.dtsi | 8 ++++++++
>>  1 file changed, 8 insertions(+)
>> 
>> diff --git a/arch/arm64/boot/dts/exynos/exynos850.dtsi b/arch/arm64/boot/dts/exynos/exynos850.dtsi
>> index cb55015c8dce..cf4a6168846c 100644
>> --- a/arch/arm64/boot/dts/exynos/exynos850.dtsi
>> +++ b/arch/arm64/boot/dts/exynos/exynos850.dtsi
>> @@ -910,6 +910,14 @@ spi_2: spi@11d20000 {
>>  			};
>>  		};
>>  	};
>> +
>> +	apm_sram: sram@2039000 {
>> +		compatible = "mmio-sram";
>> +		reg = <0x0 0x2039000 0x40000>;
>> +		#address-cells = <1>;
>> +		#size-cells = <1>;
>> +		ranges = <0x0 0x0 0x2039000 0x40000>;
>
> You miss here children.

Thank you! I guess I should convert it to smth like this:

apm_sram: sram@2039000 {
		compatible = "mmio-sram";
		reg = <0x0 0x2039000 0x40000>;
		ranges = <0x0 0x0 0x2039000 0x40000>;
		#address-cells = <1>;
		#size-cells = <1>;

		acpm_sram_region: sram-section@0 {
			reg = <0x0 0x40000>;
		};
	};

And then later reference shmem = &acpm_sram_region from acpm node.

> Also, 'ranges' should be after 'reg'.

Thanks, will fix this.

FWIW this commit is a copy of commit 48e7821b26904
https://lore.kernel.org/r/20250207-gs101-acpm-dt-v4-1-230ba8663a2d@linaro.org

Best regards,
Alexey


^ permalink raw reply

* Re: [PATCH v2 1/2] mmc: dw_mmc: implement option for configuring DMA threshold
From: Shawn Lin @ 2026-04-14  8:50 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jaehoon Chung,
	Krzysztof Kozlowski, Alim Akhtar
  Cc: shawn.lin, linux-mmc, devicetree, linux-kernel, linux-arm-kernel,
	linux-samsung-soc
In-Reply-To: <20260414-dwmmc-dma-thr-v2-1-4058078f5361@disroot.org>

在 2026/04/14 星期二 16:36, Kaustabh Chakraborty 写道:
> Some controllers, such as certain Exynos SDIO ones, are unable to
> perform DMA transfers of small amount of bytes properly. Following the
> device tree schema, implement the property to define the DMA transfer
> threshold (from a hard coded value of 16 bytes) so that lesser number of
> bytes can be transferred safely skipping DMA in such controllers. The
> value of 16 bytes stays as the default for controllers which do not
> define it. This value can be overridden by implementation-specific init
> sequences.
> 
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> ---
>   drivers/mmc/host/dw_mmc.c | 5 +++--
>   drivers/mmc/host/dw_mmc.h | 2 ++
>   2 files changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 20193ee7b73eb..9dd9fed4ccf49 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -40,7 +40,6 @@
>   				 SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
>   #define DW_MCI_ERROR_FLAGS	(DW_MCI_DATA_ERROR_FLAGS | \
>   				 DW_MCI_CMD_ERROR_FLAGS)
> -#define DW_MCI_DMA_THRESHOLD	16
>   
>   #define DW_MCI_FREQ_MAX	200000000	/* unit: HZ */
>   #define DW_MCI_FREQ_MIN	100000		/* unit: HZ */
> @@ -821,7 +820,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
>   	 * non-word-aligned buffers or lengths. Also, we don't bother
>   	 * with all the DMA setup overhead for short transfers.
>   	 */
> -	if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
> +	if (data->blocks * data->blksz < host->dma_threshold)
>   		return -EINVAL;
>   
>   	if (data->blksz & 3)
> @@ -3245,6 +3244,8 @@ int dw_mci_probe(struct dw_mci *host)
>   		goto err_clk_ciu;
>   	}
>   
> +	host->dma_threshold = 16;

I'd prefer to set it in dw_mci_alloc_host() instead of picking up
a random place to put it, for better code management.

> +
>   	if (host->rstc) {
>   		reset_control_assert(host->rstc);
>   		usleep_range(10, 50);
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index 42e58be74ce09..fc7601fba849f 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -164,6 +164,8 @@ struct dw_mci {
>   	void __iomem		*fifo_reg;
>   	u32			data_addr_override;
>   	bool			wm_aligned;
> +	/* Configurable data byte threshold value for DMA transfer. */

No here, there is a long section of comment before struct dw_mci{ } that
describes each member of it, please add it there.

> +	u32			dma_threshold;
>   
>   	struct scatterlist	*sg;
>   	struct sg_mapping_iter	sg_miter;
> 

^ permalink raw reply

* Re: [PATCH] arm64: dts: exynos850: Add SRAM node
From: Krzysztof Kozlowski @ 2026-04-14  9:08 UTC (permalink / raw)
  To: Alexey Klimov, Sam Protsenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Alim Akhtar
  Cc: linux-samsung-soc, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <DHSR70EGYY4N.2EA2HWIXJR7QR@linaro.org>

On 14/04/2026 11:00, Alexey Klimov wrote:
> On Mon Apr 13, 2026 at 4:23 PM BST, Krzysztof Kozlowski wrote:
>> On 13/04/2026 16:52, Alexey Klimov wrote:
>>> SRAM is used by the ACPM protocol to retrieve the ACPM channels
>>> information and configuration data. Add the SRAM node.
>>>
>>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>>> ---
>>>  arch/arm64/boot/dts/exynos/exynos850.dtsi | 8 ++++++++
>>>  1 file changed, 8 insertions(+)
>>>
>>> diff --git a/arch/arm64/boot/dts/exynos/exynos850.dtsi b/arch/arm64/boot/dts/exynos/exynos850.dtsi
>>> index cb55015c8dce..cf4a6168846c 100644
>>> --- a/arch/arm64/boot/dts/exynos/exynos850.dtsi
>>> +++ b/arch/arm64/boot/dts/exynos/exynos850.dtsi
>>> @@ -910,6 +910,14 @@ spi_2: spi@11d20000 {
>>>  			};
>>>  		};
>>>  	};
>>> +
>>> +	apm_sram: sram@2039000 {
>>> +		compatible = "mmio-sram";
>>> +		reg = <0x0 0x2039000 0x40000>;
>>> +		#address-cells = <1>;
>>> +		#size-cells = <1>;
>>> +		ranges = <0x0 0x0 0x2039000 0x40000>;
>>
>> You miss here children.
> 
> Thank you! I guess I should convert it to smth like this:
> 
> apm_sram: sram@2039000 {
> 		compatible = "mmio-sram";
> 		reg = <0x0 0x2039000 0x40000>;
> 		ranges = <0x0 0x0 0x2039000 0x40000>;
> 		#address-cells = <1>;
> 		#size-cells = <1>;
> 
> 		acpm_sram_region: sram-section@0 {
> 			reg = <0x0 0x40000>;

This covers entire block, so feels pointless. Maybe requirement of
children should be dropped. What's the point of having children? Why
does the driver need them?

> 		};
> 	};
> 
> And then later reference shmem = &acpm_sram_region from acpm node.
> 
>> Also, 'ranges' should be after 'reg'.
> 
> Thanks, will fix this.
> 
> FWIW this commit is a copy of commit 48e7821b26904
> https://lore.kernel.org/r/20250207-gs101-acpm-dt-v4-1-230ba8663a2d@linaro.org


Huh, we should fix that one as well.


Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH 00/35] irqchip/qcom-pdc: Clean up register mapping and DT descriptions
From: Mukesh Ojha @ 2026-04-14  9:16 UTC (permalink / raw)
  To: Konrad Dybcio, maulik.shah
  Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, cros-qcom-dts-watchers,
	linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <23c2f6c3-1bcf-4bd3-9fb3-71d6c8d1a4eb@oss.qualcomm.com>

On Mon, Apr 13, 2026 at 10:27:41AM +0200, Konrad Dybcio wrote:
> On 4/10/26 8:40 PM, Mukesh Ojha wrote:
> > The Qualcomm PDC (Power Domain Controller) hardware exposes multiple DRV
> > (Driver) regions, each 0x10000 bytes in size, where each region serves a
> > specific client in the system . Linux only needs access to the APSS DRV
> > region.
> 
> [...]
> 
> >   arm64: dts: qcom: kaanapali: Drop unused second PDC reg entry
> >   arm64: dts: qcom: lemans: Drop unused second PDC reg entry
> >   arm64: dts: qcom: milos: Drop unused second PDC reg entry
> >   arm64: dts: qcom: monaco: Drop unused second PDC reg entry
> >   arm64: dts: qcom: sc8280xp: Drop unused second PDC reg entry
> >   arm64: dts: qcom: sdx75: Drop unused second PDC reg entry
> >   arm64: dts: qcom: talos: Drop unused second PDC reg entry
> 
> I believe that was intended for this feature:
> 
> https://lore.kernel.org/linux-arm-msm/1568411962-1022-8-git-send-email-ilina@codeaurora.org/
> 
> Is that something that ever turned out useful?

You are right, this patch is being carried in downstream for almost ~7years.

+@Maulik

Do we really have need of this patch by Lina in upstream ?

> 
> Konrad

-- 
-Mukesh Ojha

^ permalink raw reply

* Re: [PATCH 2/3] dt-bindings: power: qcom,rpmhpd: Add RPMh power domain for Nord SoC
From: Konrad Dybcio @ 2026-04-14  9:26 UTC (permalink / raw)
  To: Shawn Guo, Ulf Hansson
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Kamal Wadhwa, Taniya Das, Bartosz Golaszewski,
	Deepti Jaggi, linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260414035909.652992-3-shengchao.guo@oss.qualcomm.com>

On 4/14/26 5:59 AM, Shawn Guo wrote:
> From: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> 
> Document the RPMh power domain for Nord SoC, and add definitions for
> the new power domains present on Nord SoC.
> 
>  - RPMHPD_NSP3: power domain for the 4th NSP subsystem
>  - RPMHPD_GFX1: power domain for the 2nd GFX subsystem
> 
> Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 3/3] pmdomain: qcom: rpmhpd: Add power domains for Nord SoC
From: Konrad Dybcio @ 2026-04-14  9:27 UTC (permalink / raw)
  To: Shawn Guo, Ulf Hansson
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Kamal Wadhwa, Taniya Das, Bartosz Golaszewski,
	Deepti Jaggi, linux-arm-msm, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260414035909.652992-4-shengchao.guo@oss.qualcomm.com>

On 4/14/26 5:59 AM, Shawn Guo wrote:
> From: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> 
> Add RPMh power domains required for Nord SoC.  This includes
> new definitions for power domains supplying GFX1 and NSP3 subsystem.
> 
> Co-developed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
> Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH v2 2/2] media: i2c: add os02g10 image sensor driver
From: Laurent Pinchart @ 2026-04-14  9:27 UTC (permalink / raw)
  To: Elgin Perumbilly
  Cc: sakari.ailus, tarang.raval, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Hans Verkuil, Hans de Goede,
	Vladimir Zapolskiy, Mehdi Djait, Benjamin Mugnier,
	Sylvain Petinot, Hardevsinh Palaniya, Jingjing Xiong, linux-media,
	devicetree, linux-kernel
In-Reply-To: <20260414084952.217215-3-elgin.perumbilly@siliconsignals.io>

Hi Elgin,

I sent a review comment on v1.

On Tue, Apr 14, 2026 at 02:19:45PM +0530, Elgin Perumbilly wrote:
> Add a v4l2 subdevice driver for the Omnivision os02g10 sensor.
> 
> The Omnivision os02g10 is a CMOS image sensor with an active array size of
> 1920 x 1080.
> 
> The following features are supported:
> - Manual exposure an gain control support
> - vblank/hblank control support
> - vflip/hflip control support
> - Test pattern control support
> - Supported resolution: 1920 x 1080 @ 30fps (SBGGR10)
> 
> Signed-off-by: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
> Reviewed-by: Tarang Raval <tarang.raval@siliconsignals.io>
> ---
>  MAINTAINERS                 |    1 +
>  drivers/media/i2c/Kconfig   |   10 +
>  drivers/media/i2c/Makefile  |    1 +
>  drivers/media/i2c/os02g10.c | 1039 +++++++++++++++++++++++++++++++++++
>  4 files changed, 1051 insertions(+)
>  create mode 100644 drivers/media/i2c/os02g10.c

[snip]

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH v1 2/4] arm64: dts: qcom: qcs6490: Enable DP audio
From: Konrad Dybcio @ 2026-04-14  9:28 UTC (permalink / raw)
  To: Kumar Anurag, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Srinivas Kandagatla,
	Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-sound
In-Reply-To: <20260413091937.134469-3-kumar.singh@oss.qualcomm.com>

On 4/13/26 11:19 AM, Kumar Anurag wrote:
> Add new dai link to enable DP audio.
> 
> Signed-off-by: Kumar Anurag <kumar.singh@oss.qualcomm.com>
> ---

subject: qcs6490-rb3gen2

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* [PATCH v4 0/2] hwmon: Add support for MPS mp2985
From: wenswang @ 2026-04-14  9:28 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, linux, corbet, skhan
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Wensheng Wang

From: Wensheng Wang <wenswang@yeah.net>

Add mp2985 driver in hwmon and add dt-bindings for it.

V3 -> V4:
    1. Avoid mantissa data overflow in mp2985_linear_exp_transfer()
       function.

V2 -> V3:
    1. The shifted mantissa be clamped to the range [-1024, 1023]
       before being masked in mp2985_linear_exp_transfer() function.
    2. The PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT
       value are clamped to 0xFFF before being written to the mp2985.
    3. Fix the vout scale issue for vout linear11 mode.

v1 -> v2:
    1. add Krzysztof's Acked-by
    2. remove duplicate entry in mp2985.rst
    3. clamp vout value to 32767
    4. simplify the code for obtaining PMBUS_VOUT_MODE bit value
    5. add comment for explaining MP2985 supported vout mode
    6. switch back to previous page after obtaining vid scale to avoid
       confusing the PMBus core

Wensheng Wang (2):
  dt-bindings: hwmon: Add MPS mp2985
  hwmon: add MP2985 driver

 .../devicetree/bindings/trivial-devices.yaml  |   2 +
 Documentation/hwmon/index.rst                 |   1 +
 Documentation/hwmon/mp2985.rst                | 147 +++++++
 MAINTAINERS                                   |   7 +
 drivers/hwmon/pmbus/Kconfig                   |   9 +
 drivers/hwmon/pmbus/Makefile                  |   1 +
 drivers/hwmon/pmbus/mp2985.c                  | 402 ++++++++++++++++++
 7 files changed, 569 insertions(+)
 create mode 100644 Documentation/hwmon/mp2985.rst
 create mode 100644 drivers/hwmon/pmbus/mp2985.c

-- 
2.25.1


^ permalink raw reply

* Re: [PATCH 06/11] media: iris: Fix VM count passed to firmware
From: Konrad Dybcio @ 2026-04-14  9:29 UTC (permalink / raw)
  To: Vishnu Reddy, Bryan O'Donoghue, Vikash Garodia,
	Dikshita Agarwal, Abhinav Kumar, Mauro Carvalho Chehab,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joerg Roedel,
	Will Deacon, Robin Murphy, Bjorn Andersson, Konrad Dybcio,
	Stefan Schmidt, Hans Verkuil
  Cc: linux-media, linux-arm-msm, devicetree, linux-kernel, iommu,
	stable
In-Reply-To: <20260414-glymur-v1-6-7d3d1cf57b16@oss.qualcomm.com>

On 4/14/26 7:00 AM, Vishnu Reddy wrote:
> On Glymur, firmware interprets the value written to CPU_CS_SCIACMDARG3 as
> the number of virtual machines (VMs) and internally adds 1 to it. Writing
> 1 causes firmware to treat it as 2 VMs. Since only one VM is required,
> remove this write to leave the register at its reset value of 0. This does
> not affect other platforms as only Glymur firmware uses this register,
> earlier platform firmwares ignore it.

Should we write a zero there, then?

Konrad

^ permalink raw reply

* [PATCH v4 2/2] hwmon: add MP2985 driver
From: wenswang @ 2026-04-14  9:29 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, linux, corbet, skhan
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Wensheng Wang
In-Reply-To: <20260414092921.1067735-1-wenswang@yeah.net>

From: Wensheng Wang <wenswang@yeah.net>

Add support for MPS mp2985 controller. This driver exposes
telemetry and limit value readings and writtings.

Signed-off-by: Wensheng Wang <wenswang@yeah.net>
---
V3 -> V4:
    1. Avoid mantissa data overflow in mp2985_linear_exp_transfer()
       function.

V2 -> V3:
    1. The shifted mantissa be clamped to the range [-1024, 1023]
       before being masked in mp2985_linear_exp_transfer() function.
    2. The PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT
       value are clamped to 0xFFF before being written to the mp2985.
    3. Fix the vout scale issue for vout linear11 mode.

v1 -> v2:
    1. remove duplicate entry in mp2985.rst
    2. clamp vout value to 32767
    3. simplify the code for obtaining PMBUS_VOUT_MODE bit value
    4. add comment for explaining MP2985 supported vout mode
    5. switch back to previous page after obtaining vid scale to avoid
       confusing the PMBus core

 Documentation/hwmon/index.rst  |   1 +
 Documentation/hwmon/mp2985.rst | 147 ++++++++++++
 MAINTAINERS                    |   7 +
 drivers/hwmon/pmbus/Kconfig    |   9 +
 drivers/hwmon/pmbus/Makefile   |   1 +
 drivers/hwmon/pmbus/mp2985.c   | 402 +++++++++++++++++++++++++++++++++
 6 files changed, 567 insertions(+)
 create mode 100644 Documentation/hwmon/mp2985.rst
 create mode 100644 drivers/hwmon/pmbus/mp2985.c

diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index b2ca8513cfcd..1b7007f41b39 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -183,6 +183,7 @@ Hardware Monitoring Kernel Drivers
    mp2925
    mp29502
    mp2975
+   mp2985
    mp2993
    mp5023
    mp5920
diff --git a/Documentation/hwmon/mp2985.rst b/Documentation/hwmon/mp2985.rst
new file mode 100644
index 000000000000..87a39c8a300c
--- /dev/null
+++ b/Documentation/hwmon/mp2985.rst
@@ -0,0 +1,147 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver mp2985
+====================
+
+Supported chips:
+
+  * MPS mp2985
+
+    Prefix: 'mp2985'
+
+Author:
+
+	Wensheng Wang <wenswang@yeah.net>
+
+Description
+-----------
+
+This driver implements support for Monolithic Power Systems, Inc. (MPS)
+MP2985 Dual Loop Digital Multi-phase Controller.
+
+Device compliant with:
+
+- PMBus rev 1.3 interface.
+
+The driver exports the following attributes via the 'sysfs' files
+for input voltage:
+
+**in1_input**
+
+**in1_label**
+
+**in1_crit**
+
+**in1_crit_alarm**
+
+**in1_lcrit**
+
+**in1_lcrit_alarm**
+
+**in1_max**
+
+**in1_max_alarm**
+
+**in1_min**
+
+**in1_min_alarm**
+
+The driver provides the following attributes for output voltage:
+
+**in2_input**
+
+**in2_label**
+
+**in2_crit**
+
+**in2_crit_alarm**
+
+**in2_lcrit**
+
+**in2_lcrit_alarm**
+
+**in3_input**
+
+**in3_label**
+
+**in3_crit**
+
+**in3_crit_alarm**
+
+**in3_lcrit**
+
+**in3_lcrit_alarm**
+
+The driver provides the following attributes for input current:
+
+**curr1_input**
+
+**curr1_label**
+
+The driver provides the following attributes for output current:
+
+**curr2_input**
+
+**curr2_label**
+
+**curr2_crit**
+
+**curr2_crit_alarm**
+
+**curr2_max**
+
+**curr2_max_alarm**
+
+**curr3_input**
+
+**curr3_label**
+
+**curr3_crit**
+
+**curr3_crit_alarm**
+
+**curr3_max**
+
+**curr3_max_alarm**
+
+The driver provides the following attributes for input power:
+
+**power1_input**
+
+**power1_label**
+
+**power2_input**
+
+**power2_label**
+
+The driver provides the following attributes for output power:
+
+**power3_input**
+
+**power3_label**
+
+**power4_input**
+
+**power4_label**
+
+The driver provides the following attributes for temperature:
+
+**temp1_input**
+
+**temp1_crit**
+
+**temp1_crit_alarm**
+
+**temp1_max**
+
+**temp1_max_alarm**
+
+**temp2_input**
+
+**temp2_crit**
+
+**temp2_crit_alarm**
+
+**temp2_max**
+
+**temp2_max_alarm**
diff --git a/MAINTAINERS b/MAINTAINERS
index 3adc870d523b..ead04c2d1665 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17933,6 +17933,13 @@ S:	Maintained
 F:	Documentation/hwmon/mp29502.rst
 F:	drivers/hwmon/pmbus/mp29502.c
 
+MPS MP2985 DRIVER
+M:	Wensheng Wang <wenswang@yeah.net>
+L:	linux-hwmon@vger.kernel.org
+S:	Maintained
+F:	Documentation/hwmon/mp2985.rst
+F:	drivers/hwmon/pmbus/mp2985.c
+
 MPS MP2993 DRIVER
 M:	Noah Wang <noahwang.wang@outlook.com>
 L:	linux-hwmon@vger.kernel.org
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index fc1273abe357..83fe5866c083 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -447,6 +447,15 @@ config SENSORS_MP2975
 	  This driver can also be built as a module. If so, the module will
 	  be called mp2975.
 
+config SENSORS_MP2985
+	tristate "MPS MP2985"
+	help
+	  If you say yes here you get hardware monitoring support for MPS
+	  MP2985 Dual Loop Digital Multi-Phase Controller.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called mp2985.
+
 config SENSORS_MP2993
 	tristate "MPS MP2993"
 	help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index d6c86924f887..24505bbee2b0 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_SENSORS_MP2891)	+= mp2891.o
 obj-$(CONFIG_SENSORS_MP2925)	+= mp2925.o
 obj-$(CONFIG_SENSORS_MP29502)	+= mp29502.o
 obj-$(CONFIG_SENSORS_MP2975)	+= mp2975.o
+obj-$(CONFIG_SENSORS_MP2985)	+= mp2985.o
 obj-$(CONFIG_SENSORS_MP2993)	+= mp2993.o
 obj-$(CONFIG_SENSORS_MP5023)	+= mp5023.o
 obj-$(CONFIG_SENSORS_MP5920)	+= mp5920.o
diff --git a/drivers/hwmon/pmbus/mp2985.c b/drivers/hwmon/pmbus/mp2985.c
new file mode 100644
index 000000000000..eb1a25b00c0b
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp2985.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2985)
+ *
+ * Copyright (C) 2026 MPS
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+/*
+ * Vender specific register READ_PIN_EST(0x93), READ_IIN_EST(0x8E),
+ * MFR_VR_MULTI_CONFIG_R1(0x0D) and MFR_VR_MULTI_CONFIG_R2(0x1D).
+ * The READ_PIN_EST is used to read pin telemetry, the READ_IIN_EST
+ * is used to read iin telemetry and the MFR_VR_MULTI_CONFIG_R1,
+ * MFR_VR_MULTI_CONFIG_R2 are used to obtain vid scale.
+ */
+#define READ_PIN_EST	0x93
+#define READ_IIN_EST	0x8E
+#define MFR_VR_MULTI_CONFIG_R1	0x0D
+#define MFR_VR_MULTI_CONFIG_R2	0x1D
+
+#define MP2985_VOUT_DIV	64
+#define MP2985_VOUT_OVUV_UINT	125
+#define MP2985_VOUT_OVUV_DIV	64
+
+#define MP2985_PAGE_NUM	2
+
+#define MP2985_RAIL1_FUNC	(PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | \
+							 PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
+							 PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \
+							 PMBUS_HAVE_STATUS_VOUT | \
+							 PMBUS_HAVE_STATUS_IOUT | \
+							 PMBUS_HAVE_STATUS_TEMP | \
+							 PMBUS_HAVE_STATUS_INPUT)
+
+#define MP2985_RAIL2_FUNC	(PMBUS_HAVE_PIN | PMBUS_HAVE_VOUT | \
+							 PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
+							 PMBUS_HAVE_TEMP | PMBUS_HAVE_IIN | \
+							 PMBUS_HAVE_STATUS_VOUT | \
+							 PMBUS_HAVE_STATUS_IOUT | \
+							 PMBUS_HAVE_STATUS_TEMP | \
+							 PMBUS_HAVE_STATUS_INPUT)
+
+struct mp2985_data {
+	struct pmbus_driver_info info;
+	int vout_scale[MP2985_PAGE_NUM];
+	int vid_offset[MP2985_PAGE_NUM];
+};
+
+#define to_mp2985_data(x) container_of(x, struct mp2985_data, info)
+
+static u16 mp2985_linear_exp_transfer(u16 word, u16 expect_exponent)
+{
+	s16 exponent, mantissa, target_exponent;
+
+	exponent = ((s16)word) >> 11;
+	mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
+	target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11;
+
+	/*
+	 * The MP2985 does not support negtive limit value, if a negtive
+	 * limit value is written, the limit value will become to 0. And
+	 * the maximum positive limit value is limitted to 0x3FF.
+	 */
+	if (mantissa < 0) {
+		mantissa = 0;
+	} else {
+		if (exponent > target_exponent) {
+			mantissa = (1023 >> (exponent - target_exponent)) >= mantissa ?
+						mantissa << (exponent - target_exponent) :
+						0x3FF;
+		} else {
+			mantissa = clamp_val(mantissa >> (target_exponent - exponent),
+					     0, 0x3FF);
+		}
+	}
+
+	return mantissa | ((expect_exponent << 11) & 0xf800);
+}
+
+static int mp2985_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VOUT_MODE:
+		/*
+		 * The MP2985 does not follow standard PMBus protocol completely,
+		 * and the calculation of vout in this driver is based on direct
+		 * format. As a result, the format of vout is enforced to direct.
+		 */
+		ret = PB_VOUT_MODE_DIRECT;
+		break;
+	default:
+		ret = -ENODATA;
+		break;
+	}
+
+	return ret;
+}
+
+static int mp2985_read_word_data(struct i2c_client *client, int page, int phase,
+				 int reg)
+{
+	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+	struct mp2985_data *data = to_mp2985_data(info);
+	int ret;
+
+	switch (reg) {
+	case PMBUS_READ_VOUT:
+		ret = pmbus_read_word_data(client, page, phase, reg);
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * The MP2985 supports three vout mode, direct, linear11 and vid mode.
+		 * In vid mode, the MP2985 vout telemetry has 49 vid step offset, but
+		 * PMBUS_VOUT_OV_FAULT_LIMIT and PMBUS_VOUT_UV_FAULT_LIMIT do not take
+		 * this into consideration, their resolution are 1.953125mV/LSB, as a
+		 * result, format[PSC_VOLTAGE_OUT] can not be set to vid mode directly.
+		 * Adding extra vid_offset variable for vout telemetry.
+		 */
+		ret = clamp_val(DIV_ROUND_CLOSEST(((ret & GENMASK(11, 0)) +
+									data->vid_offset[page]) *
+							data->vout_scale[page], MP2985_VOUT_DIV),
+							0, 0x7FFF);
+		break;
+	case PMBUS_READ_IIN:
+		/*
+		 * The MP2985 has standard PMBUS_READ_IIN register(0x89), but this is
+		 * not used to read the input current of per rail. The input current
+		 * is read through the vender redefined register READ_IIN_EST(0x8E).
+		 */
+		ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST);
+		break;
+	case PMBUS_READ_PIN:
+		/*
+		 * The MP2985 has standard PMBUS_READ_PIN register(0x97), but this
+		 * is not used to read the input power of per rail. The input power
+		 * of per rail is read through the vender redefined register
+		 * READ_PIN_EST(0x93).
+		 */
+		ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST);
+		break;
+	case PMBUS_VOUT_OV_FAULT_LIMIT:
+	case PMBUS_VOUT_UV_FAULT_LIMIT:
+		ret = pmbus_read_word_data(client, page, phase, reg);
+		if (ret < 0)
+			return ret;
+
+		ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * MP2985_VOUT_OVUV_UINT,
+					MP2985_VOUT_OVUV_DIV);
+		break;
+	case PMBUS_STATUS_WORD:
+	case PMBUS_READ_VIN:
+	case PMBUS_READ_IOUT:
+	case PMBUS_READ_POUT:
+	case PMBUS_READ_TEMPERATURE_1:
+	case PMBUS_VIN_OV_FAULT_LIMIT:
+	case PMBUS_VIN_OV_WARN_LIMIT:
+	case PMBUS_VIN_UV_WARN_LIMIT:
+	case PMBUS_VIN_UV_FAULT_LIMIT:
+	case PMBUS_IOUT_OC_FAULT_LIMIT:
+	case PMBUS_IOUT_OC_WARN_LIMIT:
+	case PMBUS_OT_FAULT_LIMIT:
+	case PMBUS_OT_WARN_LIMIT:
+		/*
+		 * These register is not explicitly handled by the driver,
+		 * as a result, return -ENODATA directly.
+		 */
+		ret = -ENODATA;
+		break;
+	default:
+		/*
+		 * The MP2985 do not support other telemetry and limit value
+		 * reading, so, return -EINVAL directly.
+		 */
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int mp2985_write_word_data(struct i2c_client *client, int page, int reg,
+				  u16 word)
+{
+	int ret;
+
+	switch (reg) {
+	case PMBUS_VIN_OV_FAULT_LIMIT:
+	case PMBUS_VIN_OV_WARN_LIMIT:
+	case PMBUS_VIN_UV_WARN_LIMIT:
+	case PMBUS_VIN_UV_FAULT_LIMIT:
+		/*
+		 * The PMBUS_VIN_OV_FAULT_LIMIT, PMBUS_VIN_OV_WARN_LIMIT,
+		 * PMBUS_VIN_UV_WARN_LIMIT and PMBUS_VIN_UV_FAULT_LIMIT
+		 * of MP2985 is linear11 format, and the exponent is a
+		 * constant value(5'b11101), so the exponent of word
+		 * parameter should be converted to 5'b11101(0x1D).
+		 */
+		ret = pmbus_write_word_data(client, page, reg,
+					    mp2985_linear_exp_transfer(word, 0x1D));
+		break;
+	case PMBUS_VOUT_OV_FAULT_LIMIT:
+	case PMBUS_VOUT_UV_FAULT_LIMIT:
+		/*
+		 * The bit0-bit11 is the limit value, and bit12-bit15
+		 * should not be changed.
+		 */
+		ret = pmbus_read_word_data(client, page, 0xff, reg);
+		if (ret < 0)
+			return ret;
+
+		ret = pmbus_write_word_data(client, page, reg,
+					    (ret & ~GENMASK(11, 0)) |
+				clamp_val(DIV_ROUND_CLOSEST(word * MP2985_VOUT_OVUV_DIV,
+							    MP2985_VOUT_OVUV_UINT), 0, 0xFFF));
+		break;
+	case PMBUS_OT_FAULT_LIMIT:
+	case PMBUS_OT_WARN_LIMIT:
+		/*
+		 * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of
+		 * MP2985 is linear11 format, and the exponent is a
+		 * constant value(5'b00000), so the exponent of word
+		 * parameter should be converted to 5'b00000.
+		 */
+		ret = pmbus_write_word_data(client, page, reg,
+					    mp2985_linear_exp_transfer(word, 0x00));
+		break;
+	case PMBUS_IOUT_OC_FAULT_LIMIT:
+	case PMBUS_IOUT_OC_WARN_LIMIT:
+		/*
+		 * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT
+		 * of MP2985 is linear11 format, and the exponent can not be
+		 * changed.
+		 */
+		ret = pmbus_read_word_data(client, page, 0xff, reg);
+		if (ret < 0)
+			return ret;
+
+		ret = pmbus_write_word_data(client, page, reg,
+					    mp2985_linear_exp_transfer(word,
+								       FIELD_GET(GENMASK(15, 11),
+										 ret)));
+		break;
+	default:
+		/*
+		 * The MP2985 do not support other limit value configuration,
+		 * so, return -EINVAL directly.
+		 */
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+mp2985_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
+			   int page)
+{
+	struct mp2985_data *data = to_mp2985_data(info);
+	int ret;
+
+	ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The MP2985 supports three vout mode. If PMBUS_VOUT_MODE
+	 * bit5 is 1, it is vid mode. If PMBUS PMBUS_VOUT_MODE bit4
+	 * is 1, it is linear11 mode, the vout scale is 1.953125mv/LSB.
+	 * If PMBUS PMBUS_VOUT_MODE bit6 is 1, it is direct mode, the
+	 * vout scale is 1mv/LSB. In vid mode, the MP2985 vout telemetry
+	 * has 49 vid step offset.
+	 */
+	if (FIELD_GET(BIT(5), ret)) {
+		ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);
+		if (ret < 0)
+			return ret;
+
+		ret = i2c_smbus_read_word_data(client, page == 0 ?
+						MFR_VR_MULTI_CONFIG_R1 :
+						MFR_VR_MULTI_CONFIG_R2);
+		if (ret < 0)
+			return ret;
+
+		if (page == 0) {
+			if (FIELD_GET(BIT(4), ret))
+				data->vout_scale[page] = 320;
+			else
+				data->vout_scale[page] = 640;
+		} else {
+			if (FIELD_GET(BIT(3), ret))
+				data->vout_scale[page] = 320;
+			else
+				data->vout_scale[page] = 640;
+		}
+
+		data->vid_offset[page] = 49;
+
+		/*
+		 * For vid mode, the MP2985 should be changed to page 2
+		 * to obtain vout scale value, this may confuse the PMBus
+		 * core. To avoid this, switch back to the previous page
+		 * again.
+		 */
+		ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+		if (ret < 0)
+			return ret;
+	} else if (FIELD_GET(BIT(4), ret)) {
+		data->vout_scale[page] = 125;
+		data->vid_offset[page] = 0;
+	} else {
+		data->vout_scale[page] = 64;
+		data->vid_offset[page] = 0;
+	}
+
+	return 0;
+}
+
+static int mp2985_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+	int ret;
+
+	ret = mp2985_identify_vout_scale(client, info, 0);
+	if (ret < 0)
+		return ret;
+
+	return mp2985_identify_vout_scale(client, info, 1);
+}
+
+static struct pmbus_driver_info mp2985_info = {
+	.pages = MP2985_PAGE_NUM,
+	.format[PSC_VOLTAGE_IN] = linear,
+	.format[PSC_CURRENT_IN] = linear,
+	.format[PSC_CURRENT_OUT] = linear,
+	.format[PSC_POWER] = linear,
+	.format[PSC_TEMPERATURE] = linear,
+	.format[PSC_VOLTAGE_OUT] = direct,
+
+	.m[PSC_VOLTAGE_OUT] = 1,
+	.R[PSC_VOLTAGE_OUT] = 3,
+	.b[PSC_VOLTAGE_OUT] = 0,
+
+	.func[0] = MP2985_RAIL1_FUNC,
+	.func[1] = MP2985_RAIL2_FUNC,
+	.read_word_data = mp2985_read_word_data,
+	.read_byte_data = mp2985_read_byte_data,
+	.write_word_data = mp2985_write_word_data,
+	.identify = mp2985_identify,
+};
+
+static int mp2985_probe(struct i2c_client *client)
+{
+	struct mp2985_data *data;
+
+	data = devm_kzalloc(&client->dev, sizeof(struct mp2985_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	memcpy(&data->info, &mp2985_info, sizeof(mp2985_info));
+
+	return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mp2985_id[] = {
+	{"mp2985", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, mp2985_id);
+
+static const struct of_device_id __maybe_unused mp2985_of_match[] = {
+	{.compatible = "mps,mp2985"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, mp2985_of_match);
+
+static struct i2c_driver mp2985_driver = {
+	.driver = {
+		.name = "mp2985",
+		.of_match_table = mp2985_of_match,
+	},
+	.probe = mp2985_probe,
+	.id_table = mp2985_id,
+};
+
+module_i2c_driver(mp2985_driver);
+
+MODULE_AUTHOR("Wensheng Wang <wenswang@yeah.net>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP2985 device");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
-- 
2.25.1


^ permalink raw reply related

* [PATCH v4 1/2] dt-bindings: hwmon: Add MPS mp2985
From: wenswang @ 2026-04-14  9:29 UTC (permalink / raw)
  To: robh, krzk+dt, conor+dt, linux, corbet, skhan
  Cc: devicetree, linux-kernel, linux-hwmon, linux-doc, Wensheng Wang,
	Krzysztof Kozlowski
In-Reply-To: <20260414092801.1067470-1-wenswang@yeah.net>

From: Wensheng Wang <wenswang@yeah.net>

Add support for MPS mp2985 controller.

Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Wensheng Wang <wenswang@yeah.net>
---
v1 -> v2:
    1. add Krzysztof's Acked-by

 Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index a482aeadcd44..d61482269352 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -325,6 +325,8 @@ properties:
           - mps,mp29612
             # Monolithic Power Systems Inc. multi-phase controller mp29816
           - mps,mp29816
+            # Monolithic Power Systems Inc. multi-phase controller mp2985
+          - mps,mp2985
             # Monolithic Power Systems Inc. multi-phase controller mp2993
           - mps,mp2993
             # Monolithic Power Systems Inc. hot-swap protection device
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH 0/3] arm-smmu-v3: Add PMCG child support and update PMU MMIO mapping
From: Robin Murphy @ 2026-04-14  9:32 UTC (permalink / raw)
  To: Peng Fan
  Cc: Will Deacon, Joerg Roedel, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Mark Rutland, linux-arm-kernel, iommu, devicetree,
	linux-kernel, linux-perf-users, Peng Fan
In-Reply-To: <ad3w/P1vA2uKsV/o@shlinux89>

On 2026-04-14 8:47 am, Peng Fan wrote:
> Hi Robin,
> 
> On Fri, Apr 10, 2026 at 01:07:29PM +0100, Robin Murphy wrote:
>> On 08/04/2026 2:47 pm, Peng Fan wrote:
>>> On Wed, Apr 08, 2026 at 12:15:31PM +0100, Robin Murphy wrote:
>>>> On 2026-04-08 8:51 am, Peng Fan (OSS) wrote:
>>>>> This patch series adds proper support for describing and probing the
>>>>> Arm SMMU v3 PMCG (Performance Monitor Control Group) as a child node of
>>>>> the SMMU in Devicetree, and updates the relevant drivers accordingly.
>>>>>
>>>>> The SMMU v3 architecture allows an optional PMCG block, typically
>>>>> associated with TCUs, to be implemented within the SMMU register
>>>>> address space. For example, mmu700 PMCG is at the offset 0x2000 of the
>>>>> TCU page 0.
>>>>
>>>> But what's wrong with the existing binding? Especially given that it even has
>>>> an upstream user already:
>>>>
>>>> https://git.kernel.org/torvalds/c/aef9703dcbf8
>>>>
>>>>> Patch 1 updates the SMMU v3 Devicetree binding to allow PMCG child nodes,
>>>>> referencing the existing arm,smmu-v3-pmcg binding.
>>>>>
>>>>> Patch 2 updates the arm-smmu-v3 driver to populate platform devices for
>>>>> child nodes described in DT once the SMMU probe succeeds.
>>>>>
>>>>> Patch 3 updates the SMMUv3 PMU driver to correctly handle MMIO mapping when
>>>>> PMCG is described as a child node. The PMCG registers occupy a sub-region
>>>>> of the parent SMMU MMIO window, which is already requested by the SMMU
>>>>
>>>> That has not been the case since 52f3fab0067d ("iommu/arm-smmu-v3: Don't
>>>> reserve implementation defined register space") nearly 6 years ago, where the
>>>> whole purpose was to support Arm's PMCG implementation properly. What kernel
>>>> is this based on?
>>>
>>> Seems I am wrong. I thought PMCG is in page 0, so there were resource
>>> conflicts. I just retest without this patchset, all goes well.
>>>
>>> But from dt perspective, should the TCU PMCG node be child node of
>>> SMMU node?
>>
>> No. PMCGs can be used entirely independently of the SMMU itself, and while
>> most of the events do relate to SMMU translation and thus aren't necessarily
>> meaningful if it's not in use, there are still some which can be useful for
>> basic traffic counting, monitoring GPT/translation activity from _other_
>> security states (if observation is delegated to Non-Secure) and possibly
>> other things, even if the "main" Non-Secure SMMU interface isn't advertised
>> at all. It would be unreasonable to require the SMMU node to be present and
>> enabled *and* have a driver to populate PMCGs, to monitor events which are
>> outside the scope of that driver.
> 
> Thanks for explaining this in detail.
> 
> Just have one more question, we are using mmu-700, but MMU-700 implementation
> defined TCU and TBU events are not supported.
> 
> Should we introduce a compatible string saying "arm,mmu700-tcu-pmcg" or
> "arm,mmu700-tbu-pmcg"? TBH, I have not checked MMU600(AE) or else.

MMU-700 and all other Arm implementations are still fully compatible 
with "arm,mmu-600-pmcg" in terms of what that means. That lets the 
driver correctly construct the "identifier" attribute, which then allows 
userspace to know what exact PMU implementation it is.

We don't maintain ever-growing lists of aliases for imp-def events in 
the kernel driver, same as we don't for CPU PMUs either. Generally, 
anyone who has reason to go near those is likely to already have the TRM 
to hand and thus have the encodings anyway, but I suppose you could add 
jevents with the proper meaningful descriptions if you really wanted to.

Thanks,
Robin.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox