* [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
@ 2026-07-06 7:11 Atanas Filipov
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
` (6 more replies)
0 siblings, 7 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
This series adds support for the Qualcomm JPEG V4L2 mem2mem encoder on SM8250.
The goal is to upstream a standards-based JPEG encode path using the V4L2 M2M
framework, with DT-described hardware resources and SoC DTS integration.
The JPEG encoder is part of the Camera SubSystem (CAMSS) and is described as a
child node of the CAMSS block, allowing of_platform_populate() in camss_probe()
to register and probe it automatically.
Dependencies:
- Patch 1/5 is related to bod's work queued in patchwork:
https://patchwork.linuxtv.org/project/linux-media/patch/20260326-b4-linux-next-25-03-13-dtsi-x1e80100-camss-v11-4-5b93415be6dd@linaro.org/
Supported compatibles:
- qcom,sm8250-jenc
Driver scope and design choices:
- uses the standard V4L2 mem2mem + vb2 workflow
- maps runtime resources from DT (clocks/interconnects/iommu/etc.)
Functional validation:
- tested with v4l2-ctl and GStreamer (v4l2jpegenc)
- V4L2 compliance test (v4l2-compliance) passed
Note: The handling of shared camera subsystem resources (power domains,
interconnects) for child IP blocks is still an open design question.
We have followed the established SM8250 pattern for now and would
welcome guidance from maintainers on the preferred approach.
Changes since v3:
CAMSS binding (dt-bindings: media: qcom,sm8250-camss):
- Added as new patch 2/5; allows #address-cells, #size-cells, ranges,
and child nodes using unevaluatedProperties: false so that dtbs_check
validates child nodes against their own schema without requiring the
parent binding to enumerate each child type explicitly.
Binding (dt-bindings: media: qcom,jpeg-encoder):
- Dropped soc and camss wrapper nodes from example. (Dmitry)
- Removed opp-level from all OPP table entries. (Dmitry)
CAMSS (media: qcom: camss):
- Replaced patch 1/4 with Bryan O'Donoghue's original upstream commit. (Vladimir, bod)
Driver (media: qcom: jpeg):
- Cleaned up driver patch commit. (bod)
- Removed redundant "Provides" section from Kconfig help. (Dmitry)
- Removed unneeded headers from qcom_jenc_defs.h. (Dmitry)
- Moved V4L2_CID_QCOM_JPEG_* defines to v4l2-controls.h. (Dmitry)
- Removed perf level and clock frequency constants. (Dmitry)
- Replaced register mask enum with direct mask constants. (Dmitry)
- Replaced manual clock scaling with dev_pm_opp_set_opp(). (Dmitry)
- Fixed clock teardown order in PM suspend. (Dmitry)
- Removed ICC management from driver. (Dmitry)
- Switched to _DEFINE_DEV_PM_OPS for PM operations. (Dmitry)
- Replaced kzalloc_obj() with devm_kzalloc() in probe. (Dmitry)
- Added IRQF_NO_AUTOEN to defer IRQ until hardware is acquired. (Dmitry)
- Used devm_add_action_or_reset() for v4l2_device_unregister cleanup. (Dmitry)
- Moved generic JPEG marker defines to include/media/jpeg.h. (Dmitry)
- Reused v4l2-jpeg reference Huffman tables in header generation. (Dmitry)
- Added usage examples to the driver commit log. (bod)
- Added baseline performance measurements table. (bod)
DTS (arm64: dts: qcom: sm8250):
- Restored camss interconnect-names to original values.
- Removed icp-mnoc interconnect from jpeg-encoder node. (bod)
- Added #address-cells, #size-cells and ranges before status. (Dmitry)
- Padded jpeg-encoder reg address to 8 digits: 0x0ac53000. (Dmitry)
- Removed TITAN_TOP_GDSC power-domain from jpeg-encoder sub-node. (Dmitry)
- Removed duplicate iommus entry. (Dmitry)
- Used ICC defines instead of 0 in interconnects. (Dmitry)
Changes since v2:
Binding (dt-bindings: media: qcom,jpeg-encoder):
- IOMMU stream IDs documented in binding description. (bod)
- Dropped "Properties documented:" section from commit message. (krzk)
- Dropped clocks minItems constraint. (krzk)
- Renamed clock-names to match hardware signal names. (krzk)
- Renamed interconnect-names to reflect path topology. (krzk, Dmitry)
DTS (arm64: dts: qcom: sm8250):
- Removed cell-index; use lowercase hex, no 0x0 padding. (Dmitry)
- Moved jpeg-encoder node into the camss block as a child node. (bod)
- MMCX power domain not added to jpeg-encoder; on SM8250 MMCX is
voted by camcc, not by individual IP blocks. (bod, kept as-is)
CAMSS (media: qcom: camss):
- Added of_platform_populate() in camss_probe() to register child
platform devices; no conflicts with existing CAMSS resource
management observed. (bod)
Driver (media: qcom: jpeg):
- Sort obj-y entries alphabetically in qcom/Makefile. (krzk)
- Use devm_mutex_init() instead of mutex_init(). (Frank Li)
- Check return value of devm_mutex_init(). (Frank Li)
- Kept return value check on dma_set_mask_and_coherent(); consistent
with upstream media driver practice. (Frank Li, kept as-is)
Changes since v1:
Binding (dt-bindings: media: qcom,jpeg-encoder):
- Removed qcm6490 and sm8550 compatible strings; this series is SM8250
only.
- Fixed clock-names block in the binding example.
- Added operating-points-v2 property documentation.
- Fixed reg in the example to use the correct 2-cell (64-bit) format
matching the sm8250 soc node address/size cells.
- Dropped reg-names: with a single reg entry it is unnecessary.
- Removed description literal block scalar (|).
- Dropped _clk suffix from clock-names.
- Added iommus description in the properties section with
minItems/maxItems constraints.
Driver (media: qcom: jpeg):
- Trimmed hardware register map and definitions.
- Migrated to devm_clk_bulk_get_all() replacing per-SoC named clock
arrays.
- Migrated to the OPP framework for core clock rate and performance
level management.
- Switched to devm_platform_ioremap_resource(pdev, 0).
- Switched to RUNTIME_PM_OPS() and SYSTEM_SLEEP_PM_OPS() macros.
- platform_set_drvdata() moved to immediately after allocation.
- .pm uses pm_ptr().
Known limitations:
- scaling is not supported
- width and height must be aligned to 16 pixels
Atanas Filipov (4):
dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node
dt-bindings: media: qcom: Add JPEG encoder binding
arm64: dts: qcom: sm8250: Add JPEG encoder node
media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
Bryan O'Donoghue (1):
media: qcom: camss: Populate CAMSS child devices via DT
.../bindings/media/qcom,jpeg-encoder.yaml | 146 ++
.../bindings/media/qcom,sm8250-camss.yaml | 10 +-
arch/arm64/boot/dts/qcom/sm8250.dtsi | 81 +
drivers/media/platform/qcom/Kconfig | 1 +
drivers/media/platform/qcom/Makefile | 1 +
drivers/media/platform/qcom/camss/camss.c | 3 +
drivers/media/platform/qcom/jpeg/Kconfig | 15 +
drivers/media/platform/qcom/jpeg/Makefile | 9 +
.../media/platform/qcom/jpeg/qcom_jenc_defs.h | 37 +
.../media/platform/qcom/jpeg/qcom_jenc_dev.c | 314 ++++
.../media/platform/qcom/jpeg/qcom_jenc_dev.h | 109 ++
.../media/platform/qcom/jpeg/qcom_jenc_hdr.c | 331 ++++
.../media/platform/qcom/jpeg/qcom_jenc_hdr.h | 119 ++
.../media/platform/qcom/jpeg/qcom_jenc_ops.c | 1625 +++++++++++++++++
.../media/platform/qcom/jpeg/qcom_jenc_ops.h | 52 +
.../media/platform/qcom/jpeg/qcom_jenc_res.c | 15 +
.../media/platform/qcom/jpeg/qcom_jenc_res.h | 19 +
.../qcom/jpeg/qcom_jenc_v420_hw_info.h | 410 +++++
.../media/platform/qcom/jpeg/qcom_jenc_v4l2.c | 1154 ++++++++++++
.../media/platform/qcom/jpeg/qcom_jenc_v4l2.h | 23 +
include/media/jpeg.h | 33 +
include/uapi/linux/v4l2-controls.h | 21 +
22 files changed, 4527 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
create mode 100644 drivers/media/platform/qcom/jpeg/Kconfig
create mode 100644 drivers/media/platform/qcom/jpeg/Makefile
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h
base-commit: 8d6dbbbe3ba62de0a63e962ee004afb848c8e3ac
--
2.34.1
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
@ 2026-07-06 7:11 ` Atanas Filipov
2026-07-06 12:09 ` Krzysztof Kozlowski
2026-07-06 12:41 ` Dmitry Baryshkov
2026-07-06 7:11 ` [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node Atanas Filipov
` (5 subsequent siblings)
6 siblings, 2 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Use devm_of_platform_populate() so that child nodes declared under the
CAMSS device tree node (e.g. OPE) are automatically instantiated as
platform devices. This is required now that CAMSS is modelled as a
simple-bus and ISP blocks such as OPE are described as child nodes.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
drivers/media/platform/qcom/camss/camss.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2123f6388e3d..95e655a8b6aa 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
+#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/slab.h>
@@ -5362,6 +5363,8 @@ static int camss_probe(struct platform_device *pdev)
if (!camss)
return -ENOMEM;
+ devm_of_platform_populate(dev);
+
camss->res = of_device_get_match_data(dev);
atomic_set(&camss->ref_count, 0);
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
@ 2026-07-06 7:11 ` Atanas Filipov
2026-07-06 10:07 ` Bryan O'Donoghue
2026-07-06 12:11 ` Krzysztof Kozlowski
2026-07-06 7:11 ` [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding Atanas Filipov
` (4 subsequent siblings)
6 siblings, 2 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
The CAMSS node uses #address-cells = <2>, #size-cells = <2> and ranges
to act as a bus for child IP blocks such as the JPEG encoder. Add these
properties to the binding so that dtbs_check does not reject them.
Use unevaluatedProperties: false instead of additionalProperties: false
so that child nodes are validated against their own schema (matched by
compatible) without requiring the parent binding to enumerate each child
type explicitly. This is the correct approach for a bus-like container
node and avoids updating the camss binding for every new child device
added in the future.
No functional change.
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
.../devicetree/bindings/media/qcom,sm8250-camss.yaml | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
index a509d4bbcb4a..6008938ee369 100644
--- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
@@ -102,6 +102,14 @@ properties:
- description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
- description: Titan GDSC - Titan ISP Block, Global Distributed Switch Controller.
+ "#address-cells":
+ const: 2
+
+ "#size-cells":
+ const: 2
+
+ ranges: true
+
ports:
$ref: /schemas/graph.yaml#/properties/ports
@@ -317,7 +325,7 @@ required:
- vdda-phy-supply
- vdda-pll-supply
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
2026-07-06 7:11 ` [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node Atanas Filipov
@ 2026-07-06 7:11 ` Atanas Filipov
2026-07-06 10:19 ` Bryan O'Donoghue
2026-07-06 12:17 ` Krzysztof Kozlowski
2026-07-06 7:11 ` [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node Atanas Filipov
` (3 subsequent siblings)
6 siblings, 2 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
Add device-tree binding for the Qualcomm JPEG encoder hardware block
present in SM8250 (Kona) SoCs.
The JPEG encoder is a standalone hardware IP within the camera subsystem
that performs JPEG compression in memory-to-memory fashion. It is
separate from the CAMSS ISP pipeline and has its own register space,
interrupt, clocks, power domain, IOMMU streams, and interconnect paths.
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
.../bindings/media/qcom,jpeg-encoder.yaml | 151 ++++++++++++++++++
1 file changed, 151 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
new file mode 100644
index 000000000000..e4c16388ef07
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
@@ -0,0 +1,151 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,jpeg-encoder.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm JPEG Encoder
+
+maintainers:
+ - Atanas Filipov <atanas.filipov@oss.qualcomm.com>
+
+description:
+ Qualcomm JPEG Encoder is the JPEG encode hardware present in Qualcomm SoCs.
+
+properties:
+ compatible:
+ enum:
+ - qcom,sm8250-jenc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 6
+
+ clock-names:
+ items:
+ - const: hf_axi
+ - const: sf_axi
+ - const: core_ahb
+ - const: cpas_ahb
+ - const: cnoc_axi
+ - const: jpeg
+
+ interconnects:
+ maxItems: 3
+
+ interconnect-names:
+ items:
+ - const: cpu-cfg
+ - const: hf-mnoc
+ - const: sf-mnoc
+
+ iommus:
+ maxItems: 1
+
+ operating-points-v2: true
+
+ opp-table:
+ type: object
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - interconnects
+ - interconnect-names
+ - iommus
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,camcc-sm8250.h>
+ #include <dt-bindings/clock/qcom,gcc-sm8250.h>
+ #include <dt-bindings/interconnect/qcom,icc.h>
+ #include <dt-bindings/interconnect/qcom,sm8250.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ jpeg-encoder@ac53000 {
+ compatible = "qcom,sm8250-jenc";
+ reg = <0xac53000 0x1000>;
+
+ interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
+
+ clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
+ <&gcc GCC_CAMERA_SF_AXI_CLK>,
+ <&camcc CAM_CC_CORE_AHB_CLK>,
+ <&camcc CAM_CC_CPAS_AHB_CLK>,
+ <&camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&camcc CAM_CC_JPEG_CLK>;
+ clock-names = "hf_axi",
+ "sf_axi",
+ "core_ahb",
+ "cpas_ahb",
+ "cnoc_axi",
+ "jpeg";
+
+ interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
+ <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "cpu-cfg",
+ "hf-mnoc",
+ "sf-mnoc";
+
+ iommus = <&apps_smmu 0x2040 0x400>;
+
+ operating-points-v2 = <&jpeg_opp_table>;
+
+ jpeg_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-100000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <100000000>;
+ opp-peak-kBps = <76800 104166 104166>;
+ opp-avg-kBps = <38400 33569 33569>;
+ required-opps = <&rpmhpd_opp_min_svs>;
+ };
+
+ opp-200000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <200000000>;
+ opp-peak-kBps = <76800 208333 208333>;
+ opp-avg-kBps = <38400 67138 67138>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-400000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <400000000>;
+ opp-peak-kBps = <76800 416666 416666>;
+ opp-avg-kBps = <38400 134277 134277>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-480000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <480000000>;
+ opp-peak-kBps = <76800 500000 500000>;
+ opp-avg-kBps = <38400 161132 161132>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-600000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <600000000>;
+ opp-peak-kBps = <76800 625000 625000>;
+ opp-avg-kBps = <38400 201416 201416>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
` (2 preceding siblings ...)
2026-07-06 7:11 ` [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding Atanas Filipov
@ 2026-07-06 7:11 ` Atanas Filipov
2026-07-06 12:18 ` Krzysztof Kozlowski
2026-07-06 12:45 ` Dmitry Baryshkov
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
` (2 subsequent siblings)
6 siblings, 2 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
Add the JPEG encoder hardware node to the SM8250 device tree so the
qcom-jpeg V4L2 encoder driver can bind and operate on this platform.
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/sm8250.dtsi | 81 ++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
index 7076720413ab..a089b706f60c 100644
--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
@@ -12,6 +12,7 @@
#include <dt-bindings/dma/qcom-gpi.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interconnect/qcom,osm-l3.h>
+#include <dt-bindings/interconnect/qcom,icc.h>
#include <dt-bindings/interconnect/qcom,sm8250.h>
#include <dt-bindings/mailbox/qcom-ipcc.h>
#include <dt-bindings/phy/phy-qcom-qmp.h>
@@ -4471,6 +4472,10 @@ cci1_i2c1: i2c-bus@1 {
camss: camss@ac6a000 {
compatible = "qcom,sm8250-camss";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
status = "disabled";
reg = <0 0x0ac6a000 0 0x2000>,
@@ -4649,6 +4654,82 @@ port@5 {
reg = <5>;
};
};
+
+ jpeg-encoder@ac53000 {
+ compatible = "qcom,sm8250-jenc";
+
+ reg = <0 0x0ac53000 0 0x1000>;
+
+ interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
+
+ clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
+ <&gcc GCC_CAMERA_SF_AXI_CLK>,
+ <&camcc CAM_CC_CORE_AHB_CLK>,
+ <&camcc CAM_CC_CPAS_AHB_CLK>,
+ <&camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&camcc CAM_CC_JPEG_CLK>;
+
+ clock-names = "hf_axi",
+ "sf_axi",
+ "core_ahb",
+ "cpas_ahb",
+ "cnoc_axi",
+ "jpeg";
+
+ interconnects = <&gem_noc MASTER_AMPSS_M0
+ QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_CAMERA_CFG
+ QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&mmss_noc MASTER_CAMNOC_HF
+ QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0
+ QCOM_ICC_TAG_ALWAYS>,
+ <&mmss_noc MASTER_CAMNOC_SF
+ QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0
+ QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "cpu-cfg",
+ "hf-mnoc",
+ "sf-mnoc";
+
+ iommus = <&apps_smmu 0x2040 0x400>;
+
+ operating-points-v2 = <&jpeg_opp_table>;
+
+ jpeg_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-100000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <100000000>;
+ required-opps = <&rpmhpd_opp_min_svs>;
+ };
+
+ opp-200000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <200000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-400000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <400000000>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-480000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <480000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-600000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <600000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
+ };
};
camcc: clock-controller@ad00000 {
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
` (3 preceding siblings ...)
2026-07-06 7:11 ` [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node Atanas Filipov
@ 2026-07-06 7:11 ` Atanas Filipov
2026-07-06 7:46 ` Vladimir Zapolskiy
` (4 more replies)
2026-07-06 10:12 ` [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Bryan O'Donoghue
2026-07-06 12:24 ` Krzysztof Kozlowski
6 siblings, 5 replies; 33+ messages in thread
From: Atanas Filipov @ 2026-07-06 7:11 UTC (permalink / raw)
To: linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree, Atanas Filipov
Add a Qualcomm JPEG encoder driver implemented on top of the
V4L2 mem2mem framework.
The driver wires vb2 queue handling, format negotiation, JPEG header
handling, interrupt-driven job completion, and runtime PM/clock/ICC
integration for the standalone JPEG encode hardware block.
This series targets SM8250 (Kona) platforms.
The jpeg-encoder node is described as a child node of the CAMSS block
and is probed automatically via of_platform_populate() in camss_probe().
Usage examples:
- Check of related video node: v4l2-ctl --list-devices
The expected result:
qcom-jpeg-enc (platform:qcom-jpeg-enc):
/dev/videoX
V4L2 Examples:
v4l2-ctl -d /dev/video14 \
--set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
--stream-mmap --stream-out-mmap
v4l2-ctl -d /dev/video14 \
-c compression_quality=100 \
--set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
--stream-mmap --stream-out-mmap \
--stream-count=100
v4l2-ctl -d /dev/video14 \
--set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
--stream-mmap --stream-out-mmap \
--set-ctrl=perf_level_auto=1 \
--set-ctrl=fps_target=30 \
--stream-count=100
v4l2-ctl -d /dev/video14 \
--set-fmt-video-out=width=8192,height=8192,pixelformat=NM12 \
--stream-mmap --stream-out-mmap \
--stream-count=1 \
--stream-to=8192x8192_NM12.jpg
v4l2-ctl -d /dev/video14 \
--set-fmt-video-out=width=8192,height=8192,pixelformat=GREY \
--stream-mmap --stream-out-mmap \
--stream-count=1 \
--stream-to=OUT_8192x8192.jpg
v4l2-ctl -d /dev/video14 \
-c compression_quality=100 \
--set-fmt-video-out=width=1920,height=1088,pixelformat=NM12 \
--stream-mmap=4 --stream-out-mmap=4 \
--stream-count=100 \
--stream-out-pattern=21 \
--stream-to=OUT_1920x1088_100F.mjpg
GStreamer examples:
gst-launch-1.0 videotestsrc \
is-live=false pattern=ball num-buffers=50 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
videoscale ! videoconvert ! \
video/x-raw,format=NV12,width=1920,height=1088 ! \
v4l2jpegenc ! \
fakesink sync=false
gst-launch-1.0 videotestsrc is-live=true pattern=smpte ! \
video/x-raw,format=NV12,width=1920,height=1088 ! \
v4l2jpegenc extra-controls="controls,compression_quality=85" ! \
fpsdisplaysink -v sync=false
Performance measurements on SM8250
(NV12, quality=100, GStreamer v4l2jpegenc vs jpegenc):
+------------+-----------------------------+-------------------+
| Resolution | HW v4l2jpegenc (q100) | SW jpegenc (q100) |
| | Execution | avrFPS | maxFPS | Execution | FPS |
+------------+-----------+--------+--------+-----------+-------+
| 512x512 | 2.2332 | 1139.1 | 1195.4 | 7.1055 | 358.0 |
| 1280x720 | 1.7300 | 418.5 | 457.2 | 5.0585 | 143.1 |
| 1024x1024 | 1.6991 | 374.3 | 397.4 | 4.7304 | 134.4 |
| 1920x1088 | 1.6596 | 192.8 | 193.8 | 3.7913 | 84.4 |
| 2048x2048 | 1.7277 | 92.0 | 91.6 | 3.6343 | 43.7 |
| 4096x4096 | 1.5887 | 25.2 | 25.3 | 4.2163 | 9.5 |
| 8192x8192 | 1.3104 | 7.6 | 8.0 | 2.9987 | 3.3 |
+------------+-----------+--------+--------+-----------+-------+
Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
---
.../bindings/media/qcom,jpeg-encoder.yaml | 143 +-
drivers/media/platform/qcom/Kconfig | 1 +
drivers/media/platform/qcom/Makefile | 1 +
drivers/media/platform/qcom/jpeg/Kconfig | 15 +
drivers/media/platform/qcom/jpeg/Makefile | 9 +
.../media/platform/qcom/jpeg/qcom_jenc_defs.h | 37 +
.../media/platform/qcom/jpeg/qcom_jenc_dev.c | 314 ++++
.../media/platform/qcom/jpeg/qcom_jenc_dev.h | 109 ++
.../media/platform/qcom/jpeg/qcom_jenc_hdr.c | 331 ++++
.../media/platform/qcom/jpeg/qcom_jenc_hdr.h | 119 ++
.../media/platform/qcom/jpeg/qcom_jenc_ops.c | 1625 +++++++++++++++++
.../media/platform/qcom/jpeg/qcom_jenc_ops.h | 52 +
.../media/platform/qcom/jpeg/qcom_jenc_res.c | 15 +
.../media/platform/qcom/jpeg/qcom_jenc_res.h | 19 +
.../qcom/jpeg/qcom_jenc_v420_hw_info.h | 410 +++++
.../media/platform/qcom/jpeg/qcom_jenc_v4l2.c | 1154 ++++++++++++
.../media/platform/qcom/jpeg/qcom_jenc_v4l2.h | 23 +
include/media/jpeg.h | 33 +
include/uapi/linux/v4l2-controls.h | 21 +
19 files changed, 4357 insertions(+), 74 deletions(-)
create mode 100644 drivers/media/platform/qcom/jpeg/Kconfig
create mode 100644 drivers/media/platform/qcom/jpeg/Makefile
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c
create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h
diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
index e4c16388ef07..53e83ebe4699 100644
--- a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
@@ -72,80 +72,75 @@ examples:
#include <dt-bindings/interconnect/qcom,sm8250.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
- jpeg-encoder@ac53000 {
- compatible = "qcom,sm8250-jenc";
- reg = <0xac53000 0x1000>;
-
- interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
-
- clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
- <&gcc GCC_CAMERA_SF_AXI_CLK>,
- <&camcc CAM_CC_CORE_AHB_CLK>,
- <&camcc CAM_CC_CPAS_AHB_CLK>,
- <&camcc CAM_CC_CAMNOC_AXI_CLK>,
- <&camcc CAM_CC_JPEG_CLK>;
- clock-names = "hf_axi",
- "sf_axi",
- "core_ahb",
- "cpas_ahb",
- "cnoc_axi",
- "jpeg";
-
- interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
- &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
- <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
- &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
- <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
- &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
- interconnect-names = "cpu-cfg",
- "hf-mnoc",
- "sf-mnoc";
-
- iommus = <&apps_smmu 0x2040 0x400>;
-
- operating-points-v2 = <&jpeg_opp_table>;
-
- jpeg_opp_table: opp-table {
- compatible = "operating-points-v2";
-
- opp-100000000 {
- opp-hz = /bits/ 64 <400000000>,
- /bits/ 64 <100000000>;
- opp-peak-kBps = <76800 104166 104166>;
- opp-avg-kBps = <38400 33569 33569>;
- required-opps = <&rpmhpd_opp_min_svs>;
- };
-
- opp-200000000 {
- opp-hz = /bits/ 64 <400000000>,
- /bits/ 64 <200000000>;
- opp-peak-kBps = <76800 208333 208333>;
- opp-avg-kBps = <38400 67138 67138>;
- required-opps = <&rpmhpd_opp_low_svs>;
- };
-
- opp-400000000 {
- opp-hz = /bits/ 64 <400000000>,
- /bits/ 64 <400000000>;
- opp-peak-kBps = <76800 416666 416666>;
- opp-avg-kBps = <38400 134277 134277>;
- required-opps = <&rpmhpd_opp_svs>;
- };
-
- opp-480000000 {
- opp-hz = /bits/ 64 <400000000>,
- /bits/ 64 <480000000>;
- opp-peak-kBps = <76800 500000 500000>;
- opp-avg-kBps = <38400 161132 161132>;
- required-opps = <&rpmhpd_opp_svs_l1>;
- };
-
- opp-600000000 {
- opp-hz = /bits/ 64 <400000000>,
- /bits/ 64 <600000000>;
- opp-peak-kBps = <76800 625000 625000>;
- opp-avg-kBps = <38400 201416 201416>;
- required-opps = <&rpmhpd_opp_nom>;
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ jpeg-encoder@ac53000 {
+ compatible = "qcom,sm8250-jenc";
+ reg = <0 0x0ac53000 0 0x1000>;
+
+ interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
+
+ clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
+ <&gcc GCC_CAMERA_SF_AXI_CLK>,
+ <&camcc CAM_CC_CORE_AHB_CLK>,
+ <&camcc CAM_CC_CPAS_AHB_CLK>,
+ <&camcc CAM_CC_CAMNOC_AXI_CLK>,
+ <&camcc CAM_CC_JPEG_CLK>;
+ clock-names = "hf_axi",
+ "sf_axi",
+ "core_ahb",
+ "cpas_ahb",
+ "cnoc_axi",
+ "jpeg";
+
+ interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
+ <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "cpu-cfg",
+ "hf-mnoc",
+ "sf-mnoc";
+
+ iommus = <&apps_smmu 0x2040 0x400>;
+
+ operating-points-v2 = <&jpeg_opp_table>;
+
+ jpeg_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-100000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <100000000>;
+ required-opps = <&rpmhpd_opp_min_svs>;
+ };
+
+ opp-200000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <200000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-400000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <400000000>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-480000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <480000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-600000000 {
+ opp-hz = /bits/ 64 <400000000>,
+ /bits/ 64 <600000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
};
};
};
diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig
index 4f4d3a68e6e5..f33d53a754a0 100644
--- a/drivers/media/platform/qcom/Kconfig
+++ b/drivers/media/platform/qcom/Kconfig
@@ -5,3 +5,4 @@ comment "Qualcomm media platform drivers"
source "drivers/media/platform/qcom/camss/Kconfig"
source "drivers/media/platform/qcom/iris/Kconfig"
source "drivers/media/platform/qcom/venus/Kconfig"
+source "drivers/media/platform/qcom/jpeg/Kconfig"
diff --git a/drivers/media/platform/qcom/Makefile b/drivers/media/platform/qcom/Makefile
index ea2221a202c0..011b5d237193 100644
--- a/drivers/media/platform/qcom/Makefile
+++ b/drivers/media/platform/qcom/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += camss/
obj-y += iris/
+obj-y += jpeg/
obj-y += venus/
diff --git a/drivers/media/platform/qcom/jpeg/Kconfig b/drivers/media/platform/qcom/jpeg/Kconfig
new file mode 100644
index 000000000000..d1949d277d2b
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_QCOM_JENC
+ tristate "Qualcomm V4L2 JPEG Encoder driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ depends on VIDEO_DEV
+ select VIDEOBUF2_DMA_SG
+ select V4L2_JPEG_HELPER
+ select V4L2_MEM2MEM_DEV
+ help
+ V4L2 mem2mem driver for the Qualcomm JPEG encoder hardware
+ found in Snapdragon SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called qcom-jenc.
diff --git a/drivers/media/platform/qcom/jpeg/Makefile b/drivers/media/platform/qcom/jpeg/Makefile
new file mode 100644
index 000000000000..310f6c3c1f19
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_QCOM_JENC) += qcom-jenc.o
+
+qcom-jenc-objs += \
+ qcom_jenc_dev.o \
+ qcom_jenc_v4l2.o \
+ qcom_jenc_ops.o \
+ qcom_jenc_res.o \
+ qcom_jenc_hdr.o
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
new file mode 100644
index 000000000000..2ab29bfb9b88
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_DEFS_H
+#define QCOM_JENC_DEFS_H
+
+#include <linux/types.h>
+#include <uapi/linux/v4l2-controls.h>
+
+/* Offline JPEG encoder constraints */
+#define QCOM_JPEG_HW_MAX_WIDTH 8192
+#define QCOM_JPEG_HW_MAX_HEIGHT 8192
+#define QCOM_JPEG_HW_MIN_WIDTH 256
+#define QCOM_JPEG_HW_MIN_HEIGHT 256
+
+#define QCOM_JPEG_HW_DEF_HSTEP 16
+#define QCOM_JPEG_HW_DEF_VSTEP 16
+
+#define QCOM_JPEG_HW_DEF_WIDTH 1920
+#define QCOM_JPEG_HW_DEF_HEIGHT 1088
+
+#define QCOM_JPEG_MAX_PLANES 3
+
+#define QCOM_JPEG_QUALITY_MIN 1
+#define QCOM_JPEG_QUALITY_DEF 98
+#define QCOM_JPEG_QUALITY_MAX 100
+#define QCOM_JPEG_QUALITY_MID (QCOM_JPEG_QUALITY_MAX / 2)
+#define QCOM_JPEG_QUALITY_UNT 1
+
+#define QCOM_JPEG_FPS_MIN 1
+#define QCOM_JPEG_FPS_MAX 240
+#define QCOM_JPEG_FPS_DEF 30
+#define QCOM_JPEG_FPS_UNT 1
+
+#endif /* QCOM_JENC_DEFS_H */
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
new file mode 100644
index 000000000000..ddfa84838b6b
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-mem2mem.h>
+
+#include "qcom_jenc_dev.h"
+#include "qcom_jenc_ops.h"
+#include "qcom_jenc_res.h"
+#include "qcom_jenc_v4l2.h"
+
+enum jpeg_opp_clks_id {
+ JPEG_OPP_CNOC_IDX = 0,
+ JPEG_OPP_CORE_IDX
+};
+
+static const char * const opp_clk_names[] = {
+ [JPEG_OPP_CNOC_IDX] = "cnoc_axi",
+ [JPEG_OPP_CORE_IDX] = "jpeg",
+ NULL,
+};
+
+static struct dev_pm_opp_config opp_config = {
+ .clk_names = opp_clk_names,
+ .config_clks = dev_pm_opp_config_clks_simple,
+};
+
+static int qcom_jpeg_opp_init(struct qcom_jenc_dev *jenc)
+{
+ struct dev_pm_opp *opp;
+ int rc;
+
+ rc = devm_pm_opp_set_config(jenc->dev, &opp_config);
+ if (rc)
+ return rc;
+
+ rc = devm_pm_opp_of_add_table(jenc->dev);
+ if (rc && rc != -ENODEV)
+ return rc;
+
+ /* initialize the maximum available frequency for the JPEG core */
+ jenc->max_freq = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor_indexed(jenc->dev, &jenc->max_freq, JPEG_OPP_CORE_IDX);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ dev_pm_opp_put(opp);
+
+ /* initialize the default optimized frequency for the JPEG core */
+ jenc->opt_freq = jenc->max_freq;
+
+ dev_dbg(jenc->dev, "JPEG max clocks is: %lu\n", jenc->max_freq);
+
+ return 0;
+}
+
+static int qcom_jpeg_clk_init(struct qcom_jenc_dev *jenc)
+{
+ jenc->num_clks = devm_clk_bulk_get_all(jenc->dev, &jenc->clks);
+ if (jenc->num_clks < 0)
+ return jenc->num_clks;
+
+ return 0;
+}
+
+static int qcom_jpeg_clk_on(struct qcom_jenc_dev *jenc)
+{
+ struct dev_pm_opp *opp;
+ int rc;
+
+ rc = clk_bulk_prepare_enable(jenc->num_clks, jenc->clks);
+ if (rc)
+ return rc;
+
+ /* setup the OPP according to the calculated optimal frequency */
+ opp = dev_pm_opp_find_freq_ceil_indexed(jenc->dev, &jenc->opt_freq, JPEG_OPP_CORE_IDX);
+ if (IS_ERR(opp)) {
+ rc = PTR_ERR(opp);
+ goto err_clk_disable;
+ }
+
+ rc = dev_pm_opp_set_opp(jenc->dev, opp);
+ if (rc)
+ goto err_dev_pm_opp;
+
+ dev_dbg(jenc->dev, "selected OPP clocks cnoc=%lu, core=%lu\n",
+ dev_pm_opp_get_freq_indexed(opp, JPEG_OPP_CNOC_IDX),
+ dev_pm_opp_get_freq_indexed(opp, JPEG_OPP_CORE_IDX));
+
+ dev_pm_opp_put(opp);
+
+ return 0;
+
+err_dev_pm_opp:
+ dev_pm_opp_put(opp);
+err_clk_disable:
+ clk_bulk_disable_unprepare(jenc->num_clks, jenc->clks);
+
+ return rc;
+}
+
+static void qcom_jpeg_clk_off(struct qcom_jenc_dev *jenc)
+{
+ dev_pm_opp_set_opp(jenc->dev, NULL);
+ clk_bulk_disable_unprepare(jenc->num_clks, jenc->clks);
+ jenc->opt_freq = jenc->max_freq;
+}
+
+/* qcom_jpeg_camss_get - resume the parent CAMSS device */
+static int qcom_jpeg_camss_get(struct qcom_jenc_dev *jenc)
+{
+ return pm_runtime_resume_and_get(jenc->camss_dev);
+}
+
+/* qcom_jpeg_camss_put - release the parent CAMSS device */
+static void qcom_jpeg_camss_put(struct qcom_jenc_dev *jenc)
+{
+ pm_runtime_put_sync(jenc->camss_dev);
+}
+
+static int qcom_jpeg_pm_suspend(struct device *dev)
+{
+ struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
+
+ qcom_jpeg_clk_off(jenc);
+ qcom_jpeg_camss_put(jenc);
+
+ return 0;
+}
+
+static int qcom_jpeg_pm_resume(struct device *dev)
+{
+ struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
+ int rc;
+
+ rc = qcom_jpeg_camss_get(jenc);
+ if (rc)
+ return rc;
+
+ rc = qcom_jpeg_clk_on(jenc);
+ if (rc) {
+ qcom_jpeg_camss_put(jenc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int qcom_jpeg_pm_system_suspend(struct device *dev)
+{
+ struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
+ int rc;
+
+ v4l2_m2m_suspend(jenc->m2m_dev);
+
+ rc = pm_runtime_force_suspend(dev);
+ if (rc)
+ v4l2_m2m_resume(jenc->m2m_dev);
+
+ return rc;
+}
+
+static int qcom_jpeg_pm_system_resume(struct device *dev)
+{
+ struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_resume(dev);
+ if (rc)
+ return rc;
+
+ v4l2_m2m_resume(jenc->m2m_dev);
+
+ return 0;
+}
+
+static _DEFINE_DEV_PM_OPS(qcom_jpeg_pm_ops,
+ qcom_jpeg_pm_system_suspend, qcom_jpeg_pm_system_resume,
+ qcom_jpeg_pm_suspend, qcom_jpeg_pm_resume, NULL);
+
+static int qcom_jpeg_probe(struct platform_device *pdev)
+{
+ const struct qcom_dev_resources *res;
+ struct qcom_jenc_dev *jenc;
+ int rc;
+
+ jenc = devm_kzalloc(&pdev->dev, sizeof(*jenc), GFP_KERNEL);
+ if (!jenc)
+ return -ENOMEM;
+
+ jenc->dev = &pdev->dev;
+ jenc->camss_dev = pdev->dev.parent;
+ platform_set_drvdata(pdev, jenc);
+ rc = devm_mutex_init(&pdev->dev, &jenc->dev_mutex);
+ if (rc)
+ goto err_free_jenc;
+ spin_lock_init(&jenc->hw_lock);
+ init_completion(&jenc->reset_complete);
+ init_completion(&jenc->stop_complete);
+
+ res = device_get_match_data(jenc->dev);
+ if (!res) {
+ rc = dev_err_probe(jenc->dev, -ENODEV, "unsupported SoC\n");
+ goto err_free_jenc;
+ }
+ jenc->res = res;
+
+ if (!jenc->res->hw_ops) {
+ rc = dev_err_probe(jenc->dev, -EINVAL, "missing hw resources\n");
+ goto err_free_jenc;
+ }
+
+ rc = dma_set_mask_and_coherent(jenc->dev, DMA_BIT_MASK(32));
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to set DMA mask\n");
+ goto err_free_jenc;
+ }
+
+ jenc->jpeg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(jenc->jpeg_base)) {
+ rc = dev_err_probe(jenc->dev, PTR_ERR(jenc->jpeg_base),
+ "failed to map JPEG resource\n");
+ goto err_free_jenc;
+ }
+
+ rc = qcom_jpeg_opp_init(jenc);
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to init OPP\n");
+ goto err_free_jenc;
+ }
+
+ rc = qcom_jpeg_clk_init(jenc);
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to init clocks\n");
+ goto err_free_jenc;
+ }
+
+ jenc->irq = platform_get_irq(pdev, 0);
+ if (jenc->irq < 0) {
+ rc = dev_err_probe(jenc->dev, jenc->irq, "failed to get IRQ\n");
+ goto err_free_jenc;
+ }
+
+ rc = devm_request_threaded_irq(jenc->dev, jenc->irq,
+ jenc->res->hw_ops->hw_irq_top,
+ jenc->res->hw_ops->hw_irq_bot,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(jenc->dev), jenc);
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to request IRQ\n");
+ goto err_free_jenc;
+ }
+
+ rc = v4l2_device_register(jenc->dev, &jenc->v4l2_dev);
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to register V4L2 device\n");
+ goto err_free_jenc;
+ }
+
+ rc = devm_add_action_or_reset(jenc->dev,
+ (void (*)(void *))v4l2_device_unregister,
+ &jenc->v4l2_dev);
+ if (rc)
+ goto err_free_jenc;
+
+ rc = devm_pm_runtime_enable(jenc->dev);
+ if (rc)
+ goto err_free_jenc;
+
+ rc = qcom_jpeg_v4l2_register(jenc);
+ if (rc) {
+ dev_err_probe(jenc->dev, rc, "failed to register video device\n");
+ goto err_free_jenc;
+ }
+
+ dev_dbg(jenc->dev, "Qualcomm JPEG encoder registered\n");
+
+ return 0;
+
+err_free_jenc:
+ return rc;
+}
+
+static const struct of_device_id qcom_jpeg_of_match[] = {
+ {
+ .compatible = "qcom,sm8250-jenc",
+ .data = &qcom_t165_t480_jpeg_drvdata
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_jpeg_of_match);
+
+static struct platform_driver qcom_jpeg_platform_driver = {
+ .probe = qcom_jpeg_probe,
+ .driver = {
+ .name = QCOM_JPEG_ENC_NAME,
+ .of_match_table = qcom_jpeg_of_match,
+ .pm = pm_ptr(&qcom_jpeg_pm_ops),
+ },
+};
+
+module_platform_driver(qcom_jpeg_platform_driver);
+
+MODULE_DESCRIPTION("QCOM JPEG mem2mem V4L2 encoder");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h
new file mode 100644
index 000000000000..1b5dff346fb9
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_DEV_H
+#define QCOM_JENC_DEV_H
+
+#include <linux/device.h>
+#include <linux/irqreturn.h>
+#include <linux/mutex.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "qcom_jenc_defs.h"
+#include "qcom_jenc_hdr.h"
+#include "qcom_jenc_res.h"
+
+#define QCOM_JPEG_ENC_NAME "qcom-jpeg-enc"
+
+#define TYPE2QID(t) \
+ (V4L2_TYPE_IS_OUTPUT(t) ? JENC_SRC_QUEUE : JENC_DST_QUEUE)
+
+enum qcom_enc_qid {
+ JENC_SRC_QUEUE = 0,
+ JENC_DST_QUEUE,
+ JENC_QUEUE_MAX
+};
+
+struct jenc_enc_format {
+ u32 type;
+ u32 fourcc;
+};
+
+struct qcom_jpeg_buff {
+ struct {
+ struct sg_table *sgt;
+ dma_addr_t dma;
+ unsigned long size;
+
+ } plns[QCOM_JPEG_MAX_PLANES];
+};
+
+struct qcom_jenc_queue {
+ struct v4l2_pix_format_mplane vf;
+ u32 sequence;
+ struct qcom_jpeg_buff buff[VB2_MAX_FRAME];
+ int buff_id;
+};
+
+struct qcom_jenc_dev {
+ struct device *dev;
+ struct device *camss_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *vdev;
+ const struct qcom_dev_resources *res;
+ int irq;
+ void __iomem *jpeg_base;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ unsigned long max_freq;
+ unsigned long opt_freq;
+ /* device mutex lock */
+ struct mutex dev_mutex;
+ atomic_t ref_count;
+ struct completion reset_complete;
+ struct completion stop_complete;
+ /* encoder hardware lock */
+ spinlock_t hw_lock;
+ struct jenc_context *actx;
+
+ u32 pending_irq_status;
+
+ void (*enc_hw_irq_cb)
+ (void *data, enum vb2_buffer_state ev, size_t out_size);
+};
+
+struct jenc_context {
+ struct device *dev;
+ struct qcom_jenc_dev *jenc;
+ struct v4l2_fh fh;
+
+ /* quality update lock */
+ struct mutex quality_mutex;
+ struct v4l2_ctrl *quality_ctl;
+ u32 quality_requested;
+ u32 quality_programmed;
+ struct v4l2_ctrl *perf_level_auto_ctl;
+ struct v4l2_ctrl *fps_target_ctl;
+ struct v4l2_ctrl_handler ctrl_hdl;
+
+ /* session context lock */
+ struct mutex ctx_lock;
+
+ bool is_stopping;
+ bool hw_acquired;
+
+ struct qcom_jenc_queue bufq[JENC_QUEUE_MAX];
+ struct qcom_jenc_header hdr_cache;
+
+ struct work_struct finish_work;
+ struct work_struct stop_work;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
new file mode 100644
index 000000000000..c9959518c64d
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <media/jpeg.h>
+#include <media/v4l2-jpeg.h>
+
+#include "qcom_jenc_dev.h"
+#include "qcom_jenc_hdr.h"
+
+/*
+ * The elements defined in this header are specified
+ * in the ITU-T T.81 / JPEG specification.
+ *
+ * https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+
+#define JFIF_HEADER_WIDTH_OFFS 0x07
+#define JFIF_HEADER_HEIGHT_OFFS 0x05
+
+#define JFIF_APP0_LENGTH_HI 0x00
+#define JFIF_APP0_LENGTH_LO 0x10
+#define JFIF_IDENT_TERM 0x00
+#define JFIF_VERSION_MAJOR 0x01
+#define JFIF_VERSION_MINOR 0x01
+#define JFIF_DENSITY_HI 0x00
+#define JFIF_DENSITY_LO 0x01
+#define JFIF_THUMBNAIL_SIZE 0x00
+
+#define JPEG_SEG_LEN_HI 0x00
+#define JPEG_LEN_DQT_LUMA_LO 0x43
+#define JPEG_LEN_DQT_CHROMA_LO 0x43
+#define JPEG_LEN_SOF0_MONO_LO 0x0b
+#define JPEG_LEN_SOF0_COLOR_LO 0x11
+#define JPEG_LEN_DHT_MONO_LO 0xd2
+#define JPEG_LEN_DHT_COLOR_HI 0x01
+#define JPEG_LEN_DHT_COLOR_LO 0xa2
+#define JPEG_LEN_SOS_MONO_LO 0x08
+#define JPEG_LEN_SOS_COLOR_LO 0x0c
+
+struct jpeg_header_buf {
+ u8 *ptr;
+ u32 size;
+ u32 pos;
+};
+
+static const struct jpeg_soi_app0 soi_app0 = {
+ .soi = { JPEG_MARKER_PREFIX, JPEG_MARKER_SOI },
+ .app0_marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_APP0 },
+ .app0_length = { JFIF_APP0_LENGTH_HI, JFIF_APP0_LENGTH_LO },
+ .jfif_id = { 'J', 'F', 'I', 'F', JFIF_IDENT_TERM },
+ .version = { JFIF_VERSION_MAJOR, JFIF_VERSION_MINOR },
+ .units = 0x00,
+ .density_x = { JFIF_DENSITY_HI, JFIF_DENSITY_LO },
+ .density_y = { JFIF_DENSITY_HI, JFIF_DENSITY_LO },
+ .thumb_x = JFIF_THUMBNAIL_SIZE,
+ .thumb_y = JFIF_THUMBNAIL_SIZE,
+};
+
+static const struct jpeg_record_hdr dqt_luma_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_DQT },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_DQT_LUMA_LO }
+};
+
+static const struct jpeg_record_hdr dqt_chroma_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_DQT },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_DQT_CHROMA_LO }
+};
+
+/* Luminance quantization table */
+static const struct jpeg_dqt_header dqt_luma_data = {
+ .index = 0x00,
+};
+
+/* Chrominance quantization table */
+static const struct jpeg_dqt_header dqt_chroma_data = {
+ .index = 0x01,
+};
+
+static const struct jpeg_record_hdr sof0_mono_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_SOF0 },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_SOF0_MONO_LO },
+};
+
+static const struct jpeg_sof0_mono sof0_mono_data = {
+ .precision = JPEG_SAMPLE_PRECISION_BITS,
+ .height = { 0x00, 0x00 },
+ .width = { 0x00, 0x00 },
+ .components = JPEG_COMP_MONO,
+ .y_id = 1,
+ .y_sampling = JPEG_SAMPLING_H1V1,
+ .y_qtable = JPEG_QTABLE_LUMA,
+};
+
+static const struct jpeg_record_hdr sof0_color_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_SOF0 },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_SOF0_COLOR_LO },
+};
+
+static const struct jpeg_sof0_color sof0_color_data = {
+ .precision = JPEG_SAMPLE_PRECISION_BITS,
+ .height = { 0x00, 0x00 },
+ .width = { 0x00, 0x00 },
+ .components = JPEG_COMP_COLOR,
+ .y_id = 1,
+ .y_sampling = JPEG_SAMPLING_H2V2,
+ .y_qtable = JPEG_QTABLE_LUMA,
+ .cb_id = 2,
+ .cb_sampling = JPEG_SAMPLING_H1V1,
+ .cb_qtable = JPEG_QTABLE_CHROMA,
+ .cr_id = 3,
+ .cr_sampling = JPEG_SAMPLING_H1V1,
+ .cr_qtable = JPEG_QTABLE_CHROMA,
+};
+
+static const struct jpeg_record_hdr coeff_mono_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_DHT },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_DHT_MONO_LO },
+};
+
+static const struct jpeg_record_hdr coeff_color_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_DHT },
+ .length = { JPEG_LEN_DHT_COLOR_HI, JPEG_LEN_DHT_COLOR_LO },
+};
+
+static const struct jpeg_record_hdr sos_mono_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_SOS },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_SOS_MONO_LO },
+};
+
+static const struct jpeg_sos_mono sos_mono_data = {
+ .components = JPEG_COMP_MONO,
+ .y_id = 1,
+ .y_tables = JPEG_TABLE_SEL_LUMA,
+ .spectral = { JPEG_SPECTRAL_START, JPEG_SPECTRAL_END },
+ .approx = JPEG_APPROX_HIGH_LOW,
+};
+
+static const struct jpeg_record_hdr sos_color_hdr = {
+ .marker = { JPEG_MARKER_PREFIX, JPEG_MARKER_SOS },
+ .length = { JPEG_SEG_LEN_HI, JPEG_LEN_SOS_COLOR_LO },
+};
+
+static const struct jpeg_sos_color sos_color_data = {
+ .components = JPEG_COMP_COLOR,
+ .y_id = 1,
+ .y_tables = JPEG_TABLE_SEL_LUMA,
+ .cb_id = 2,
+ .cb_tables = JPEG_TABLE_SEL_CHROMA,
+ .cr_id = 3,
+ .cr_tables = JPEG_TABLE_SEL_CHROMA,
+ .spectral = { JPEG_SPECTRAL_START, JPEG_SPECTRAL_END },
+ .approx = JPEG_APPROX_HIGH_LOW,
+};
+
+static inline int jb_put_mem(struct jpeg_header_buf *hdr, const void *src, u32 len)
+{
+ if (len > hdr->size - hdr->pos)
+ return -ENOSPC;
+
+ memcpy(hdr->ptr + hdr->pos, src, len);
+ hdr->pos += len;
+
+ return 0;
+}
+
+static int jb_put_dht(struct jpeg_header_buf *hdr, u8 index, const u8 *table, u32 len)
+{
+ u8 data[1 + V4L2_JPEG_REF_HT_AC_LEN];
+
+ if (len > V4L2_JPEG_REF_HT_AC_LEN)
+ return -EINVAL;
+
+ data[0] = index;
+ memcpy(&data[1], table, len);
+
+ return jb_put_mem(hdr, data, len + 1);
+}
+
+static inline void patch_u16be(u8 *buf, u32 off, u16 v)
+{
+ buf[off] = (v >> 8) & 0xff;
+ buf[off + 1] = v & 0xff;
+}
+
+int qcom_jenc_header_init(struct qcom_jenc_header *c, u32 fourcc)
+{
+ int rc;
+ struct jpeg_header_buf hdr = {
+ .ptr = c->data,
+ .size = sizeof(c->data),
+ .pos = 0,
+ };
+
+ c->sof_offset = 0;
+ c->dqt_luma_offs = 0;
+ c->dqt_chroma_offs = 0;
+
+ rc = jb_put_mem(&hdr, &soi_app0, sizeof(soi_app0));
+ if (rc)
+ return rc;
+
+ /* luma DQT is always present */
+ rc = jb_put_mem(&hdr, &dqt_luma_hdr, sizeof(dqt_luma_hdr));
+ if (rc)
+ return rc;
+
+ c->dqt_luma_offs = hdr.pos;
+ rc = jb_put_mem(&hdr, &dqt_luma_data, sizeof(dqt_luma_data));
+ if (rc)
+ return rc;
+
+ /* chroma DQT only for color images */
+ if (fourcc != V4L2_PIX_FMT_GREY) {
+ rc = jb_put_mem(&hdr, &dqt_chroma_hdr, sizeof(dqt_chroma_hdr));
+ if (rc)
+ return rc;
+
+ c->dqt_chroma_offs = hdr.pos;
+ rc = jb_put_mem(&hdr, &dqt_chroma_data, sizeof(dqt_chroma_data));
+ if (rc)
+ return rc;
+ }
+
+ /* store the offset of the SOF record for later use */
+ c->sof_offset = hdr.pos;
+
+ if (fourcc != V4L2_PIX_FMT_GREY) {
+ rc = jb_put_mem(&hdr, &sof0_color_hdr, sizeof(sof0_color_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sof0_color_data, sizeof(sof0_color_data));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &coeff_color_hdr, sizeof(coeff_color_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_DC_HT_INDEX_LUMA,
+ v4l2_jpeg_ref_table_luma_dc_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_luma_dc_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_AC_HT_INDEX_LUMA,
+ v4l2_jpeg_ref_table_luma_ac_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_luma_ac_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_DC_HT_INDEX_CHROMA,
+ v4l2_jpeg_ref_table_chroma_dc_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_dc_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_AC_HT_INDEX_CHROMA,
+ v4l2_jpeg_ref_table_chroma_ac_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_ac_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sos_color_hdr, sizeof(sos_color_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sos_color_data, sizeof(sos_color_data));
+ if (rc)
+ return rc;
+ } else {
+ rc = jb_put_mem(&hdr, &sof0_mono_hdr, sizeof(sof0_mono_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sof0_mono_data, sizeof(sof0_mono_data));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &coeff_mono_hdr, sizeof(coeff_mono_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_DC_HT_INDEX_LUMA,
+ v4l2_jpeg_ref_table_luma_dc_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_luma_dc_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_dht(&hdr, JPEG_AC_HT_INDEX_LUMA,
+ v4l2_jpeg_ref_table_luma_ac_ht,
+ ARRAY_SIZE(v4l2_jpeg_ref_table_luma_ac_ht));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sos_mono_hdr, sizeof(sos_mono_hdr));
+ if (rc)
+ return rc;
+ rc = jb_put_mem(&hdr, &sos_mono_data, sizeof(sos_mono_data));
+ if (rc)
+ return rc;
+ }
+
+ c->size = hdr.pos;
+
+ return 0;
+}
+
+void qcom_jenc_dqts_emit(const struct qcom_jenc_header *c, u8 *dst)
+{
+ /* Propagate DQT tables into the JPEG header */
+ if (c->dqt_luma_offs) {
+ u32 luma_offs = c->dqt_luma_offs + sizeof(dqt_luma_data.index);
+
+ memcpy(dst + luma_offs, &c->data[luma_offs], sizeof(dqt_luma_data.value));
+ }
+
+ if (c->dqt_chroma_offs) {
+ u32 chroma_offs = c->dqt_chroma_offs + sizeof(dqt_chroma_data.index);
+
+ memcpy(dst + chroma_offs, &c->data[chroma_offs], sizeof(dqt_chroma_data.value));
+ }
+}
+
+u32 qcom_jenc_header_emit(const struct qcom_jenc_header *c, u8 *dst, u32 dst_size, u16 width,
+ u16 height)
+{
+ /* Copy JFIF into JPEG header and update actual image size */
+ if (dst_size < c->size)
+ return 0;
+
+ memcpy(dst, c->data, c->size);
+
+ /* Update output image size */
+ patch_u16be(dst, c->sof_offset + JFIF_HEADER_WIDTH_OFFS, width);
+ patch_u16be(dst, c->sof_offset + JFIF_HEADER_HEIGHT_OFFS, height);
+
+ return c->size;
+}
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h
new file mode 100644
index 000000000000..db1e5298e51f
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_HDR_H
+#define QCOM_JENC_HDR_H
+
+#include <linux/types.h>
+
+#include <media/v4l2-jpeg.h>
+
+#include "qcom_jenc_defs.h"
+
+#define JPEG_HEADER_MAX 1024
+
+struct qcom_jenc_header {
+ u8 data[JPEG_HEADER_MAX];
+ u32 size;
+ u32 sof_offset;
+ u32 dqt_luma_offs;
+ u32 dqt_chroma_offs;
+};
+
+struct jpeg_record_hdr {
+ u8 marker[2];
+ u8 length[2];
+} __packed;
+
+struct jpeg_dqt_header {
+ u8 index;
+ u8 value[V4L2_JPEG_PIXELS_IN_BLOCK];
+} __packed;
+
+struct jpeg_soi_app0 {
+ u8 soi[2];
+ u8 app0_marker[2];
+ u8 app0_length[2];
+ u8 jfif_id[5];
+ u8 version[2];
+ u8 units;
+ u8 density_x[2];
+ u8 density_y[2];
+ u8 thumb_x;
+ u8 thumb_y;
+} __packed;
+
+struct jpeg_sof0_mono {
+ u8 precision;
+ u8 height[2];
+ u8 width[2];
+ u8 components;
+
+ u8 y_id;
+ u8 y_sampling;
+ u8 y_qtable;
+} __packed;
+
+struct jpeg_sof0_color {
+ u8 precision;
+ u8 height[2];
+ u8 width[2];
+ u8 components;
+
+ u8 y_id;
+ u8 y_sampling;
+ u8 y_qtable;
+
+ u8 cb_id;
+ u8 cb_sampling;
+ u8 cb_qtable;
+
+ u8 cr_id;
+ u8 cr_sampling;
+ u8 cr_qtable;
+} __packed;
+
+struct jpeg_sos_hdr {
+ u8 sos_marker[2];
+ u8 sos_length[2];
+ u8 components;
+} __packed;
+
+struct jpeg_sos_mono {
+ u8 components;
+
+ u8 y_id;
+ u8 y_tables;
+
+ u8 spectral[2];
+ u8 approx;
+} __packed;
+
+struct jpeg_sos_color {
+ u8 components;
+
+ u8 y_id;
+ u8 y_tables;
+
+ u8 cb_id;
+ u8 cb_tables;
+
+ u8 cr_id;
+ u8 cr_tables;
+
+ u8 spectral[2];
+ u8 approx;
+} __packed;
+
+struct jenc_context;
+
+int qcom_jenc_header_init(struct qcom_jenc_header *c, u32 fourcc);
+
+void qcom_jenc_dqts_emit(const struct qcom_jenc_header *c, u8 *dst);
+
+u32 qcom_jenc_header_emit(const struct qcom_jenc_header *c, u8 *dst, u32 dst_size, u16 width,
+ u16 height);
+
+#endif /* QCOM_JENC_HDR_H */
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c
new file mode 100644
index 000000000000..1f9841d27974
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c
@@ -0,0 +1,1625 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/math64.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/scatterlist.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "qcom_jenc_defs.h"
+#include "qcom_jenc_dev.h"
+#include "qcom_jenc_ops.h"
+#include "qcom_jenc_v420_hw_info.h"
+
+/* Per-plane register offset lookup arrays (indexed by plane number 0..2) */
+static const u32 jpeg_v420_fe_pntr[] = {
+ JPEG_V420_REG_FE_PNTR_0,
+ JPEG_V420_REG_FE_PNTR_1,
+ JPEG_V420_REG_FE_PNTR_2,
+};
+
+static const u32 jpeg_v420_fe_offs[] = {
+ JPEG_V420_REG_FE_OFFS_0,
+ JPEG_V420_REG_FE_OFFS_1,
+ JPEG_V420_REG_FE_OFFS_2,
+};
+
+static const u32 jpeg_v420_fe_bsize[] = {
+ JPEG_V420_REG_FE_BSIZE_0,
+ JPEG_V420_REG_FE_BSIZE_1,
+ JPEG_V420_REG_FE_BSIZE_2,
+};
+
+static const u32 jpeg_v420_fe_stride[] = {
+ JPEG_V420_REG_FE_STRIDE_0,
+ JPEG_V420_REG_FE_STRIDE_1,
+ JPEG_V420_REG_FE_STRIDE_2,
+};
+
+static const u32 jpeg_v420_fe_hinit[] = {
+ JPEG_V420_REG_FE_HINIT_0,
+ JPEG_V420_REG_FE_HINIT_1,
+ JPEG_V420_REG_FE_HINIT_2,
+};
+
+static const u32 jpeg_v420_fe_vinit[] = {
+ JPEG_V420_REG_FE_VINIT_0,
+ JPEG_V420_REG_FE_VINIT_1,
+ JPEG_V420_REG_FE_VINIT_2,
+};
+
+static const u32 jpeg_v420_we_pntr[] = {
+ JPEG_V420_REG_WE_PNTR_0,
+ JPEG_V420_REG_WE_PNTR_1,
+ JPEG_V420_REG_WE_PNTR_2,
+};
+
+static const u32 jpeg_v420_we_stride[] = {
+ JPEG_V420_REG_WE_STRIDE_0,
+ JPEG_V420_REG_WE_STRIDE_1,
+ JPEG_V420_REG_WE_STRIDE_2,
+};
+
+static const u32 jpeg_v420_we_hinit[] = {
+ JPEG_V420_REG_WE_HINIT_0,
+ JPEG_V420_REG_WE_HINIT_1,
+ JPEG_V420_REG_WE_HINIT_2,
+};
+
+static const u32 jpeg_v420_we_hstep[] = {
+ JPEG_V420_REG_WE_HSTEP_0,
+ JPEG_V420_REG_WE_HSTEP_1,
+ JPEG_V420_REG_WE_HSTEP_2,
+};
+
+static const u32 jpeg_v420_we_vinit[] = {
+ JPEG_V420_REG_WE_VINIT_0,
+ JPEG_V420_REG_WE_VINIT_1,
+ JPEG_V420_REG_WE_VINIT_2,
+};
+
+static const u32 jpeg_v420_we_vstep[] = {
+ JPEG_V420_REG_WE_VSTEP_0,
+ JPEG_V420_REG_WE_VSTEP_1,
+ JPEG_V420_REG_WE_VSTEP_2,
+};
+
+static const u32 jpeg_v420_scale_hstep[] = {
+ JPEG_V420_REG_SCALE_HSTEP_0,
+ JPEG_V420_REG_SCALE_HSTEP_1,
+ JPEG_V420_REG_SCALE_HSTEP_2,
+};
+
+static const u32 jpeg_v420_scale_vstep[] = {
+ JPEG_V420_REG_SCALE_VSTEP_0,
+ JPEG_V420_REG_SCALE_VSTEP_1,
+ JPEG_V420_REG_SCALE_VSTEP_2,
+};
+
+static const u32 jpeg_v420_scale_out_cfg[] = {
+ JPEG_V420_REG_SCALE_OUT_CFG_0,
+ JPEG_V420_REG_SCALE_OUT_CFG_1,
+ JPEG_V420_REG_SCALE_OUT_CFG_2,
+};
+
+#define JPEG_RESET_TIMEOUT_MS 300
+#define JPEG_STOP_TIMEOUT_MS 200
+
+#define JPEG_DQT_SHIFT 20
+
+#define JPEG_MCU_BLOCK_8 8
+#define JPEG_MCU_BLOCK_16 16
+#define JPEG_MCU_BLOCK_128 128
+#define JPEG_MCU_BLOCK_256 256
+
+#define JPEG_DEFAULT_SCALE_STEP 0x200000
+
+#define JPEG_CLR_U32 (0U)
+#define JPEG_SET_U32 (~0U)
+
+/*
+ * JPEG | V4L2
+ * ---- | -------
+ * H1V1 | GREY
+ * H1V2 | YUV422M
+ * H2V1 | NV16M
+ * H2V2 | NV12M
+ */
+enum qcom_jpeg_encode_fmt {
+ JPEG_ENCODE_H1V1 = 0,
+ JPEG_ENCODE_H1V2,
+ JPEG_ENCODE_H2V1,
+ JPEG_ENCODE_H2V2,
+ JPEG_ENCODE_MONO,
+};
+
+enum qcom_jpeg_memory_fmt {
+ JPEG_MEM_FMT_PLANAR = 0x0,
+ JPEG_MEM_FMT_PPLANAR = 0x1,
+ JPEG_MEM_FMT_MONO = 0x2,
+ JPEG_MEM_FMT_COEFFICIENT = 0x3
+};
+
+enum jpeg_mal_bounds {
+ JPEG_CFG_MAL_BOUND_32_BYTES = 0x0,
+ JPEG_CFG_MAL_BOUND_64_BYTES = 0x1,
+ JPEG_CFG_MAL_BOUND_128_BYTES = 0x2,
+ JPEG_CFG_MAL_BOUND_256_BYTES = 0x3,
+ JPEG_CFG_MAL_BOUND_512_BYTES = 0x4,
+ JPEG_CFG_MAL_BOUND_1K_BYTES = 0x5,
+ JPEG_CFG_MAL_BOUND_2K_BYTES = 0x6,
+ JPEG_CFG_MAL_BOUND_4K_BYTES = 0x7
+};
+
+struct qcom_jpeg_scale_blocks {
+ u8 w_block[QCOM_JPEG_MAX_PLANES];
+ u8 h_block[QCOM_JPEG_MAX_PLANES];
+};
+
+struct qcom_jpeg_mal_boundary {
+ u32 bytes;
+ int boundary;
+};
+
+struct qcom_jpeg_formats {
+ enum qcom_jpeg_encode_fmt encode;
+ enum qcom_jpeg_memory_fmt memory;
+ u32 fourcc;
+};
+
+/*
+ * Luminance quantization table defined by CCITT T.81.
+ * See: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+static const u8 t81k1_dct_luma_table[V4L2_JPEG_PIXELS_IN_BLOCK] = {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99
+};
+
+/*
+ * Chrominance quantization table defined by CCITT T.81.
+ * See: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+static const u8 t81k2_dct_chroma_table[V4L2_JPEG_PIXELS_IN_BLOCK] = {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+
+/*
+ * Zig-zag scan order for quantized DCT coefficients
+ * as defined by CCITT T.81.
+ * See: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+static const u8 t81a6_dct_zig_zag_table[] = {
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+
+static const struct qcom_jpeg_formats jpeg_encode_fmt[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .encode = JPEG_ENCODE_MONO,
+ .memory = JPEG_MEM_FMT_MONO
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .encode = JPEG_ENCODE_H1V1,
+ .memory = JPEG_MEM_FMT_PPLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422M,
+ .encode = JPEG_ENCODE_H1V2,
+ .memory = JPEG_MEM_FMT_PLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .encode = JPEG_ENCODE_H1V2,
+ .memory = JPEG_MEM_FMT_PLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .encode = JPEG_ENCODE_H2V1,
+ .memory = JPEG_MEM_FMT_PPLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .encode = JPEG_ENCODE_H2V1,
+ .memory = JPEG_MEM_FMT_PPLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .encode = JPEG_ENCODE_H2V2,
+ .memory = JPEG_MEM_FMT_PPLANAR
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .encode = JPEG_ENCODE_H2V2,
+ .memory = JPEG_MEM_FMT_PPLANAR
+ }
+};
+
+static const struct qcom_jpeg_mal_boundary jpeg_mal_bounds[] = {
+ { .bytes = 32, .boundary = JPEG_CFG_MAL_BOUND_32_BYTES },
+ { .bytes = 64, .boundary = JPEG_CFG_MAL_BOUND_64_BYTES },
+ { .bytes = 128, .boundary = JPEG_CFG_MAL_BOUND_128_BYTES },
+ { .bytes = 256, .boundary = JPEG_CFG_MAL_BOUND_256_BYTES },
+ { .bytes = 512, .boundary = JPEG_CFG_MAL_BOUND_512_BYTES },
+ { .bytes = 1024, .boundary = JPEG_CFG_MAL_BOUND_1K_BYTES },
+ { .bytes = 2048, .boundary = JPEG_CFG_MAL_BOUND_2K_BYTES },
+ { .bytes = 4096, .boundary = JPEG_CFG_MAL_BOUND_4K_BYTES }
+};
+
+static const struct qcom_jpeg_scale_blocks jpeg_mcu_blocks[] = {
+ [JPEG_ENCODE_H1V1] = {
+ .w_block = { JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ .h_block = { JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ },
+ [JPEG_ENCODE_H1V2] = {
+ .w_block = { JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ .h_block = { JPEG_MCU_BLOCK_16, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ },
+ [JPEG_ENCODE_H2V1] = {
+ .w_block = { JPEG_MCU_BLOCK_16, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ .h_block = { JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ },
+ [JPEG_ENCODE_H2V2] = {
+ .w_block = { JPEG_MCU_BLOCK_16, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ .h_block = { JPEG_MCU_BLOCK_16, JPEG_MCU_BLOCK_8, JPEG_MCU_BLOCK_8 },
+ },
+ [JPEG_ENCODE_MONO] = {
+ .w_block = { JPEG_MCU_BLOCK_8 },
+ .h_block = { JPEG_MCU_BLOCK_8 }
+ },
+};
+
+static inline int jpeg_get_memory_fmt(u32 fourcc)
+{
+ u32 fi;
+
+ for (fi = 0; fi < ARRAY_SIZE(jpeg_encode_fmt); fi++) {
+ if (jpeg_encode_fmt[fi].fourcc == fourcc)
+ return jpeg_encode_fmt[fi].memory;
+ }
+
+ return -EINVAL;
+}
+
+static inline int jpeg_get_encode_fmt(u32 fourcc)
+{
+ u32 fi;
+
+ for (fi = 0; fi < ARRAY_SIZE(jpeg_encode_fmt); fi++) {
+ if (jpeg_encode_fmt[fi].fourcc == fourcc)
+ return jpeg_encode_fmt[fi].encode;
+ }
+
+ return -EINVAL;
+}
+
+static inline int jpeg_get_mal_boundary(u32 width, const struct qcom_jpeg_mal_boundary *table,
+ u32 count)
+{
+ u32 bi;
+
+ for (bi = 0; bi < count; bi++) {
+ if (table[bi].bytes > width)
+ break;
+ }
+
+ if (!bi)
+ return table[0].boundary;
+
+ if (bi >= count)
+ return table[count - 1].boundary;
+
+ return table[bi - 1].boundary;
+}
+
+static inline int jpeg_get_mcu_geometry(enum qcom_jpeg_encode_fmt fmt, u32 width, u32 height,
+ u32 *blk_w, u32 *blk_h, u32 *mcu_cols, u32 *mcu_rows)
+{
+ const struct qcom_jpeg_scale_blocks *blks;
+ u32 bw = 0, bh = 0;
+ u8 pln;
+
+ blks = &jpeg_mcu_blocks[fmt];
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ bw = max(bw, blks->w_block[pln]);
+ bh = max(bh, blks->h_block[pln]);
+ }
+
+ if (!bw || !bh)
+ return -EINVAL;
+
+ if (blk_w)
+ *blk_w = bw;
+ if (blk_h)
+ *blk_h = bh;
+
+ if (mcu_cols)
+ *mcu_cols = ALIGN(width, bw) / bw;
+
+ if (mcu_rows)
+ *mcu_rows = ALIGN(height, bh) / bh;
+
+ return 0;
+}
+
+static inline u32 jpeg_io_read(struct qcom_jenc_dev *jenc, u32 offset)
+{
+ return readl(jenc->jpeg_base + offset);
+}
+
+static inline void jpeg_io_write(struct qcom_jenc_dev *jenc, u32 offset, u32 value)
+{
+ writel(value, jenc->jpeg_base + offset);
+}
+
+/*
+ * Runtime bitfield helpers (for non-constant masks).
+ *
+ * Requirements:
+ * - mask must be non-zero
+ * - mask must be contiguous (e.g. 0x7u << n)
+ */
+
+static inline u32 jpeg_bits_get(u32 mask, u32 val)
+{
+ /* __ffs(0) is undefined; fail-safe on invalid masks. */
+ if (WARN_ON_ONCE(!mask))
+ return 0;
+
+ return (val & mask) >> __ffs(mask);
+}
+
+static inline u32 jpeg_bits_set(u32 mask, u32 val)
+{
+ /* __ffs(0) is undefined; fail-safe on invalid masks. */
+ if (WARN_ON_ONCE(!mask))
+ return 0;
+
+ return (val << __ffs(mask)) & mask;
+}
+
+static inline u32 jpeg_rd_bits(struct qcom_jenc_dev *jenc, u32 offs, u32 mask)
+{
+ u32 reg = jpeg_io_read(jenc, offs);
+
+ return jpeg_bits_get(mask, reg);
+}
+
+/*
+ * Read-modify-write (for R/W registers)
+ */
+static inline void jpeg_rw_bits(struct qcom_jenc_dev *jenc, u32 offs, u32 mask, u32 val)
+{
+ u32 reg = jpeg_io_read(jenc, offs);
+
+ reg &= ~mask;
+ reg |= jpeg_bits_set(mask, val);
+
+ jpeg_io_write(jenc, offs, reg);
+}
+
+/*
+ * Write-only variant (for write only registers)
+ */
+static inline void jpeg_wo_bits(struct qcom_jenc_dev *jenc, u32 offs, u32 mask, u32 val)
+{
+ jpeg_io_write(jenc, offs, jpeg_bits_set(mask, val));
+}
+
+static u8 jpeg_calculate_dqt(struct jenc_context *ectx, u8 dqt_value)
+{
+ u8 calc_val;
+ u64 ratio;
+
+ ratio = (QCOM_JPEG_QUALITY_MAX - ectx->quality_requested) << JPEG_DQT_SHIFT;
+ ratio = max_t(u64, 1, ratio);
+ do_div(ratio, QCOM_JPEG_QUALITY_MID);
+
+ calc_val = DIV64_U64_ROUND_CLOSEST(ratio * dqt_value, 1LU << JPEG_DQT_SHIFT);
+
+ return max_t(u8, 1, calc_val);
+}
+
+/*
+ * jpeg_update_dqt_cache - compute scaled DQT coefficients and store them in
+ * the software JPEG header cache (hdr_cache). Safe to call from buf_prepare
+ * before the hardware is powered on; no MMIO access is performed here.
+ */
+static void jpeg_update_dqt_cache(struct jenc_context *ectx)
+{
+ u8 *base;
+ u8 dqt_val, idx;
+ int i;
+
+ /* Luma DQT cache update */
+ if (ectx->hdr_cache.dqt_luma_offs) {
+ base = &ectx->hdr_cache.data[ectx->hdr_cache.dqt_luma_offs + 1];
+ for (i = 0; i < ARRAY_SIZE(t81k1_dct_luma_table); i++) {
+ dqt_val = jpeg_calculate_dqt(ectx, t81k1_dct_luma_table[i]);
+ idx = t81a6_dct_zig_zag_table[i];
+ base[idx] = dqt_val;
+ }
+ }
+
+ /* Chroma DQT cache update */
+ if (ectx->hdr_cache.dqt_chroma_offs) {
+ base = &ectx->hdr_cache.data[ectx->hdr_cache.dqt_chroma_offs + 1];
+ for (i = 0; i < ARRAY_SIZE(t81k2_dct_chroma_table); i++) {
+ dqt_val = jpeg_calculate_dqt(ectx, t81k2_dct_chroma_table[i]);
+ idx = t81a6_dct_zig_zag_table[i];
+ base[idx] = dqt_val;
+ }
+ }
+}
+
+/*
+ * jpeg_upload_dmi_table - write the scaled DQT coefficients to the hardware
+ * DMI registers. Must only be called from the job execution path where
+ * runtime PM has already been acquired (pm_runtime_resume_and_get).
+ *
+ * Reads precomputed values from hdr_cache (populated by jpeg_update_dqt_cache)
+ * to avoid redundant per-coefficient recalculation on the hot encode path.
+ */
+static void jpeg_upload_dmi_table(struct jenc_context *ectx)
+{
+ const u8 *luma_qt = &ectx->hdr_cache.data[ectx->hdr_cache.dqt_luma_offs + 1];
+ u32 pcfg = JPEG_DMI_ENABLE_UPLOAD;
+ u32 addr = 0x00000000;
+ u32 reg_val;
+ int i;
+
+ /* DMI upload start sequence */
+ jpeg_io_write(ectx->jenc, JPEG_V420_REG_DMI_ADDR, addr);
+ jpeg_io_write(ectx->jenc, JPEG_V420_REG_DMI_CFG, pcfg);
+
+ /* DMI Luma upload - values are stored in zigzag order in hdr_cache */
+ for (i = 0; i < ARRAY_SIZE(t81k1_dct_luma_table); i++) {
+ reg_val = div_u64(U16_MAX + 1U, luma_qt[i]);
+ reg_val = clamp_t(u32, reg_val, 0, U16_MAX);
+ jpeg_io_write(ectx->jenc, JPEG_V420_REG_DMI_DATA, reg_val);
+ }
+
+ /* DMI Chroma upload - only present for color formats */
+ if (ectx->hdr_cache.dqt_chroma_offs) {
+ const u8 *chroma_qt = &ectx->hdr_cache.data[ectx->hdr_cache.dqt_chroma_offs + 1];
+
+ for (i = 0; i < ARRAY_SIZE(t81k2_dct_chroma_table); i++) {
+ reg_val = div_u64(U16_MAX + 1U, chroma_qt[i]);
+ reg_val = clamp_t(u32, reg_val, 0, U16_MAX);
+ jpeg_io_write(ectx->jenc, JPEG_V420_REG_DMI_DATA, reg_val);
+ }
+ }
+
+ /* DMI upload end sequence */
+ jpeg_io_write(ectx->jenc, JPEG_V420_REG_DMI_CFG, addr);
+
+ ectx->quality_programmed = ectx->quality_requested;
+
+ dev_dbg(ectx->dev, "quality_programmed=%d\n", ectx->quality_programmed);
+}
+
+static void jpeg_sync_sg(struct device *dev,
+ struct qcom_jpeg_buff *frame,
+ enum dma_data_direction direction, bool for_device)
+{
+ u8 pln;
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ struct sg_table *sgt = frame->plns[pln].sgt;
+
+ if (!frame->plns[pln].dma || !sgt)
+ break;
+
+ if (for_device)
+ dma_sync_sgtable_for_device(dev, sgt, direction);
+ else
+ dma_sync_sgtable_for_cpu(dev, sgt, direction);
+ }
+}
+
+static int jpeg_init(struct qcom_jenc_dev *jenc)
+{
+ unsigned long rtime;
+ u32 hw_ver;
+
+ if (WARN_ON_ONCE(!jenc || !jenc->dev || !jenc->jpeg_base))
+ return -EINVAL;
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_CLR, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_SET_U32);
+
+ reinit_completion(&jenc->reset_complete);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_INT_MASK,
+ JPEG_V420_IRQ_STATUS_RESET_ACK_MSK, JPEG_SET_U32);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_RESET_CMD,
+ JPEG_V420_RST_CMD_FE_RESET_MSK |
+ JPEG_V420_RST_CMD_WE_RESET_MSK |
+ JPEG_V420_RST_CMD_ENCODER_RESET_MSK |
+ JPEG_V420_RST_CMD_BLOCK_FORMATTER_RST_MSK |
+ JPEG_V420_RST_CMD_SCALE_RESET_MSK |
+ JPEG_V420_RST_CMD_REGISTER_RESET_MSK |
+ JPEG_V420_RST_CMD_MISR_RESET_MSK |
+ JPEG_V420_RST_CMD_CORE_RESET_MSK |
+ JPEG_V420_RST_CMD_JPEG_V420_DOMAIN_RESET_MSK,
+ JPEG_SET_U32);
+
+ rtime = wait_for_completion_timeout(&jenc->reset_complete,
+ msecs_to_jiffies(JPEG_RESET_TIMEOUT_MS));
+ if (!rtime) {
+ dev_err(jenc->dev, "encoder HW reset timeout\n");
+ return -ETIME;
+ }
+
+ hw_ver = jpeg_io_read(jenc, JPEG_V420_REG_HW_VERSION);
+ dev_dbg(jenc->dev, "JPEG HW encoder version %d.%d.%d\n",
+ jpeg_bits_get(JPEG_V420_HW_VER_MAJOR_MSK, hw_ver),
+ jpeg_bits_get(JPEG_V420_HW_VER_MINOR_MSK, hw_ver),
+ jpeg_bits_get(JPEG_V420_HW_VER_STEP_MSK, hw_ver));
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK, JPEG_SET_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK, JPEG_CLR_U32);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK, JPEG_SET_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK, JPEG_CLR_U32);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_CLR, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_SET_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_MASK, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_SET_U32);
+
+ return 0;
+}
+
+static int jpeg_exec(struct qcom_jenc_dev *jenc)
+{
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_HW_START_MSK, 1);
+
+ return 0;
+}
+
+static void jpeg_stop(struct qcom_jenc_dev *jenc)
+{
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_HW_START_MSK, 0);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK, JPEG_SET_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK, JPEG_CLR_U32);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK, JPEG_SET_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK |
+ JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK, JPEG_CLR_U32);
+}
+
+static int jpeg_deinit(struct qcom_jenc_dev *jenc)
+{
+ unsigned long rtime;
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_CLR, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_SET_U32);
+
+ reinit_completion(&jenc->stop_complete);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_INT_MASK, JPEG_V420_IRQ_STATUS_STOP_ACK_MSK, JPEG_SET_U32);
+
+ jpeg_wo_bits(jenc, JPEG_V420_REG_HW_CMD, JPEG_V420_CMD_HW_STOP_MSK, 1);
+
+ rtime = wait_for_completion_timeout(&jenc->stop_complete,
+ msecs_to_jiffies(JPEG_STOP_TIMEOUT_MS));
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_MASK, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_CLR_U32);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_CLR, JPEG_V420_IRQ_STATUS_ALL_BITS, JPEG_SET_U32);
+
+ if (!rtime) {
+ dev_err(jenc->dev, "encoder HW stop timeout\n");
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+static int jpeg_apply_fe_addr(struct jenc_context *ectx, struct qcom_jenc_queue *q,
+ struct vb2_buffer *vb)
+{
+ struct qcom_jpeg_buff *frame = &q->buff[vb->index];
+ struct v4l2_pix_format_mplane *fmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ unsigned long flags;
+ u8 pln;
+
+ jpeg_sync_sg(jenc->dev, frame, DMA_TO_DEVICE, true);
+
+ for (pln = 0; pln < fmt->num_planes; pln++) {
+ if (!frame->plns[pln].sgt || !frame->plns[pln].sgt->sgl)
+ break;
+
+ jpeg_io_write(jenc, jpeg_v420_fe_pntr[pln], frame->plns[pln].dma);
+ jpeg_io_write(jenc, jpeg_v420_fe_offs[pln], 0);
+
+ dev_dbg(jenc->dev, "FE pln=%d addr=%pad idx:%d\n",
+ pln, &frame->plns[pln].dma, vb->index);
+ }
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ q->buff_id = vb->index;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ return 0;
+}
+
+static int jpeg_store_fe_next(struct jenc_context *ectx, struct vb2_buffer *vb2)
+{
+ struct qcom_jenc_queue *q = &ectx->bufq[TYPE2QID(vb2->type)];
+ struct qcom_jpeg_buff *buff = &q->buff[vb2->index];
+ u8 pln;
+
+ for (pln = 0; pln < q->vf.num_planes; pln++) {
+ buff->plns[pln].sgt = vb2_dma_sg_plane_desc(vb2, pln);
+ if (!buff->plns[pln].sgt || !buff->plns[pln].sgt->sgl)
+ return -EINVAL;
+
+ buff->plns[pln].dma = sg_dma_address(buff->plns[pln].sgt->sgl);
+ if (!buff->plns[pln].dma)
+ return -EINVAL;
+
+ buff->plns[pln].size = vb2_plane_size(vb2, pln);
+ if (!buff->plns[pln].size)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int jpeg_setup_fe_size(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *sfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ jpeg_rw_bits(jenc, jpeg_v420_fe_bsize[pln],
+ JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_MSK, 0);
+ jpeg_rw_bits(jenc, jpeg_v420_fe_bsize[pln],
+ JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_MSK, 0);
+ jpeg_rw_bits(jenc, jpeg_v420_fe_stride[pln], JPEG_V420_PLN_RD_STRIDE_STRIDE_MSK, 0);
+ }
+
+ for (pln = 0; pln < sfmt->num_planes; pln++) {
+ jpeg_rw_bits(jenc, jpeg_v420_fe_bsize[pln], JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_MSK,
+ sfmt->width - 1);
+ jpeg_rw_bits(jenc, jpeg_v420_fe_bsize[pln], JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_MSK,
+ sfmt->height - 1);
+ jpeg_rw_bits(jenc, jpeg_v420_fe_stride[pln], JPEG_V420_PLN_RD_STRIDE_STRIDE_MSK,
+ sfmt->plane_fmt[pln].bytesperline);
+
+ dev_dbg(ectx->dev, "FE pln=%d width=%d height=%d stride=%d\n",
+ pln,
+ jpeg_rd_bits(jenc, jpeg_v420_fe_bsize[pln],
+ JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_MSK),
+ jpeg_rd_bits(jenc, jpeg_v420_fe_bsize[pln],
+ JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_MSK),
+ jpeg_rd_bits(jenc, jpeg_v420_fe_stride[pln],
+ JPEG_V420_PLN_RD_STRIDE_STRIDE_MSK));
+ }
+
+ return 0;
+}
+
+static int jpeg_setup_fe_hinit(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++)
+ jpeg_io_write(jenc, jpeg_v420_fe_hinit[pln], 0);
+
+ return 0;
+}
+
+static int jpeg_setup_fe_vinit(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++)
+ jpeg_io_write(jenc, jpeg_v420_fe_vinit[pln], 0);
+
+ return 0;
+}
+
+static int jpeg_setup_fe_params(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *sfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 expected_planes, pln;
+ int rval;
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MAL_EN_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_BOTTOM_VPAD_EN_MSK, 1);
+
+ rval = jpeg_get_memory_fmt(sfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "invalid memory format for v4l2 format: %p4cc\n",
+ &sfmt->pixelformat);
+ return -EINVAL;
+ }
+
+ switch (rval) {
+ case JPEG_MEM_FMT_MONO:
+ expected_planes = 1;
+ break;
+ case JPEG_MEM_FMT_PPLANAR:
+ expected_planes = 2;
+ break;
+ case JPEG_MEM_FMT_PLANAR:
+ expected_planes = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (sfmt->num_planes != expected_planes) {
+ dev_err(ectx->dev, "plane mismatch fmt=%u expected=%u got=%u\n",
+ rval, expected_planes, sfmt->num_planes);
+ return -EINVAL;
+ }
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MEMORY_FORMAT_MSK, rval);
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_PLN0_EN_MSK, 0);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_PLN1_EN_MSK, 0);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_PLN2_EN_MSK, 0);
+
+ /* Scaling is not supported; always configure for 1:1 */
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_SIXTEEN_MCU_EN_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MCUS_PER_BLOCK_MSK, 0);
+
+ dev_dbg(ectx->dev, "sixteen MCU enabled=%d, %d MCU per blocks\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_SIXTEEN_MCU_EN_MSK),
+ jpeg_rd_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MCUS_PER_BLOCK_MSK));
+
+ rval = jpeg_get_mal_boundary(sfmt->width, jpeg_mal_bounds, ARRAY_SIZE(jpeg_mal_bounds));
+ if (rval < 0) {
+ dev_err(ectx->dev, "failed to get FE mal boundary width=%u\n",
+ sfmt->width);
+ return -EINVAL;
+ }
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MAL_BOUNDARY_MSK, rval);
+
+ dev_dbg(ectx->dev, "optimal FE mal boundary=%d\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_MAL_BOUNDARY_MSK));
+
+ rval = jpeg_get_encode_fmt(sfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "unsupported encode format %p4cc\n",
+ &sfmt->pixelformat);
+ return -EINVAL;
+ }
+
+ switch (rval) {
+ case JPEG_ENCODE_MONO:
+ case JPEG_ENCODE_H1V1:
+ case JPEG_ENCODE_H2V1:
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_VBPAD_CFG, JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_MSK,
+ DIV_ROUND_UP(sfmt->height, JPEG_MCU_BLOCK_8));
+ break;
+ case JPEG_ENCODE_H1V2:
+ case JPEG_ENCODE_H2V2:
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_VBPAD_CFG, JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_MSK,
+ DIV_ROUND_UP(sfmt->height, JPEG_MCU_BLOCK_16));
+ break;
+ default:
+ dev_err(ectx->dev, "unsupported encode format: %d\n", rval);
+ return -EINVAL;
+ }
+
+ dev_dbg(ectx->dev, "FE vpad config=%d\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_FE_VBPAD_CFG,
+ JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_MSK));
+
+ if (sfmt->pixelformat == V4L2_PIX_FMT_NV21M || sfmt->pixelformat == V4L2_PIX_FMT_NV61M)
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_CBCR_ORDER_MSK, 1);
+ else
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG, JPEG_V420_FE_CFG_CBCR_ORDER_MSK, 0);
+
+ for (pln = 0; pln < sfmt->num_planes; pln++) {
+ if (sfmt->width && sfmt->height) {
+ switch (pln) {
+ case 0:
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG,
+ JPEG_V420_FE_CFG_PLN0_EN_MSK, 1);
+ break;
+ case 1:
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG,
+ JPEG_V420_FE_CFG_PLN1_EN_MSK, 1);
+ break;
+ case 2:
+ jpeg_rw_bits(jenc, JPEG_V420_REG_FE_CFG,
+ JPEG_V420_FE_CFG_PLN2_EN_MSK, 1);
+ break;
+ }
+ }
+ }
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_CORE_CFG, JPEG_V420_CORE_CFG_FE_ENABLE_MSK, 1);
+
+ return 0;
+}
+
+static int jpeg_setup_fe(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ int rc;
+
+ rc = jpeg_setup_fe_size(ectx, q);
+ if (rc)
+ return rc;
+
+ rc = jpeg_setup_fe_hinit(ectx, q);
+ if (rc)
+ return rc;
+
+ rc = jpeg_setup_fe_vinit(ectx, q);
+ if (rc)
+ return rc;
+
+ rc = jpeg_setup_fe_params(ectx, q);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int jpeg_ensure_header_cache(struct jenc_context *ectx)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ int rc;
+
+ if (ectx->hdr_cache.size)
+ return 0;
+
+ rc = qcom_jenc_header_init(&ectx->hdr_cache, sq->vf.pixelformat);
+ if (rc) {
+ dev_err(ectx->dev, "JFIF header lazy init failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int jpeg_apply_we_addr(struct jenc_context *ectx, struct qcom_jenc_queue *q,
+ struct vb2_buffer *vb)
+{
+ struct qcom_jpeg_buff *frame = &q->buff[vb->index];
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ void *mptr = vb2_plane_vaddr(vb, 0);
+ dma_addr_t dma = frame->plns[0].dma;
+ unsigned long flags;
+ size_t header_size;
+ int rc;
+ u8 pln = 0;
+
+ if (WARN_ON_ONCE(!dma))
+ return -EPERM;
+
+ if (WARN_ON_ONCE(!mptr))
+ return -EPERM;
+
+ rc = jpeg_ensure_header_cache(ectx);
+ if (rc)
+ return rc;
+
+ /* Update DQT cache if quality changed, then upload to HW DMI. */
+ mutex_lock(&ectx->quality_mutex);
+ if (!ectx->hdr_cache.size || ectx->quality_programmed != ectx->quality_requested)
+ jpeg_update_dqt_cache(ectx);
+ jpeg_upload_dmi_table(ectx);
+ mutex_unlock(&ectx->quality_mutex);
+
+ /*
+ * Invalidate stale CPU cache lines before writing the JPEG header
+ * with the CPU into the destination buffer.
+ */
+ jpeg_sync_sg(jenc->dev, frame, DMA_BIDIRECTIONAL, false);
+
+ header_size = min_t(size_t, vb->planes[0].length, ectx->hdr_cache.size);
+ header_size = qcom_jenc_header_emit(&ectx->hdr_cache, mptr,
+ (u32)header_size, (u16)q->vf.width,
+ (u16)q->vf.height);
+
+ dma += header_size;
+ qcom_jenc_dqts_emit(&ectx->hdr_cache, mptr);
+
+ /*
+ * Flush CPU writes to the header before handing the buffer to the
+ * hardware DMA engine.
+ */
+ jpeg_sync_sg(jenc->dev, frame, DMA_BIDIRECTIONAL, true);
+
+ jpeg_io_write(jenc, jpeg_v420_we_pntr[pln], dma);
+
+ dev_dbg(jenc->dev, "WE pln=%d addr=%pad idx:%d\n",
+ pln, &dma, vb->index);
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ q->buff_id = vb->index;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ return 0;
+}
+
+static int jpeg_store_we_next(struct jenc_context *ectx, struct vb2_buffer *vb2)
+{
+ struct qcom_jenc_queue *q = &ectx->bufq[TYPE2QID(vb2->type)];
+ struct qcom_jpeg_buff *frame = &q->buff[vb2->index];
+ struct sg_table *sgt;
+ dma_addr_t dma;
+
+ sgt = vb2_dma_sg_plane_desc(vb2, 0);
+ if (!sgt || !sgt->sgl)
+ return -EINVAL;
+
+ dma = sg_dma_address(sgt->sgl);
+ if (!dma)
+ return -EINVAL;
+
+ if (!vb2_plane_vaddr(vb2, 0))
+ return -EINVAL;
+
+ frame->plns[0].sgt = sgt;
+ frame->plns[0].dma = dma;
+ frame->plns[0].size = vb2_plane_size(vb2, 0);
+
+ return 0;
+}
+
+static int jpeg_setup_we_size(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *dfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++)
+ jpeg_rw_bits(jenc, jpeg_v420_we_stride[pln], JPEG_V420_PLN_WR_STRIDE_STRIDE_MSK, 0);
+
+ jpeg_io_write(jenc, JPEG_V420_REG_WE_BSIZE_0, dfmt->plane_fmt[0].sizeimage);
+
+ dev_dbg(ectx->dev, "WE size=%u\n", dfmt->plane_fmt[0].sizeimage);
+
+ return 0;
+}
+
+static int jpeg_setup_we_hinit(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *dfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ if (!dfmt->width) {
+ dev_err(ectx->dev, "invalid destination width=%d\n", dfmt->width);
+ return -EINVAL;
+ }
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ jpeg_rw_bits(jenc, jpeg_v420_we_hinit[pln], JPEG_V420_PLN_WR_HINIT_INTEGER_MSK, 0);
+ jpeg_rw_bits(jenc, jpeg_v420_we_hstep[pln], JPEG_V420_PLN_WR_HSTEP_INTEGER_MSK, 0);
+ }
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_HSTEP_0,
+ JPEG_V420_PLN_WR_HSTEP_INTEGER_MSK, dfmt->width);
+
+ dev_dbg(ectx->dev, "WE hstep=%u\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_WE_HSTEP_0, JPEG_V420_PLN_WR_HSTEP_INTEGER_MSK));
+
+ return 0;
+}
+
+static int jpeg_setup_we_vinit(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *dfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u8 pln;
+
+ if (!dfmt->height) {
+ dev_err(ectx->dev, "invalid destination height=%d\n", dfmt->height);
+ return -EINVAL;
+ }
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ jpeg_rw_bits(jenc, jpeg_v420_we_vinit[pln], JPEG_V420_PLN_WR_VINIT_INTEGER_MSK, 0);
+ jpeg_rw_bits(jenc, jpeg_v420_we_vstep[pln], JPEG_V420_PLN_WR_VSTEP_INTEGER_MSK, 0);
+ }
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_VSTEP_0,
+ JPEG_V420_PLN_WR_VSTEP_INTEGER_MSK, dfmt->height);
+
+ dev_dbg(ectx->dev, "WE vstep=%u\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_WE_VSTEP_0, JPEG_V420_PLN_WR_VSTEP_INTEGER_MSK));
+
+ return 0;
+}
+
+static int jpeg_setup_we_params(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ struct v4l2_pix_format_mplane *dfmt = &q->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u32 blk_w = 0, blk_h = 0, mcu_cols = 0, mcu_rows = 0;
+ int rval;
+
+ rval = jpeg_get_memory_fmt(dfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "invalid memory format for v4l2 format: %p4cc\n",
+ &dfmt->pixelformat);
+ return -EINVAL;
+ }
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_MEMORY_FORMAT_MSK, rval);
+
+ rval = jpeg_get_mal_boundary(dfmt->width, jpeg_mal_bounds, ARRAY_SIZE(jpeg_mal_bounds));
+ if (rval < 0) {
+ dev_err(ectx->dev, "failed to get WE mal boundary width=%u\n",
+ dfmt->width);
+ return -EINVAL;
+ }
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_MAL_BOUNDARY_MSK, rval);
+
+ dev_dbg(ectx->dev, "optimal WE mal boundary=%d\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_MAL_BOUNDARY_MSK));
+
+ rval = jpeg_get_encode_fmt(dfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "unsupported encode format %p4cc\n",
+ &dfmt->pixelformat);
+ return rval;
+ }
+
+ rval = jpeg_get_mcu_geometry(rval, dfmt->width, dfmt->height, &blk_w, &blk_h,
+ &mcu_cols, &mcu_rows);
+ if (rval < 0) {
+ dev_err(ectx->dev, "invalid MCU geometry mcu_cols=%d mcu_rows=%d\n",
+ mcu_cols, mcu_rows);
+ return rval;
+ }
+
+ dev_dbg(ectx->dev, "WE blk_w=%u blk_h=%u cols=%u rows=%u\n",
+ blk_w, blk_h, mcu_cols, mcu_rows);
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_BLOCKS_0,
+ JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_MSK, mcu_rows - 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_BLOCKS_0,
+ JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_MSK, mcu_cols - 1);
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_CBCR_ORDER_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_MAL_EN_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_POP_BUFF_ON_EOS_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_WE_CFG, JPEG_V420_WE_CFG_PLN0_EN_MSK, 1);
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_CORE_CFG, JPEG_V420_CORE_CFG_MODE_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_CORE_CFG, JPEG_V420_CORE_CFG_WE_ENABLE_MSK, 1);
+
+ return 0;
+}
+
+static int jpeg_setup_we(struct jenc_context *ectx, struct qcom_jenc_queue *q)
+{
+ int rc;
+
+ rc = jpeg_setup_we_size(ectx, q);
+ if (rc)
+ return rc;
+
+ rc = jpeg_setup_we_hinit(ectx, q);
+ if (rc)
+ return rc;
+
+ rc = jpeg_setup_we_vinit(ectx, q);
+ if (rc)
+ return rc;
+
+ return jpeg_setup_we_params(ectx, q);
+}
+
+static int jpeg_setup_scale(struct jenc_context *ectx)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ struct qcom_jenc_queue *dq = &ectx->bufq[JENC_DST_QUEUE];
+ struct v4l2_pix_format_mplane *sfmt = &sq->vf;
+ struct v4l2_pix_format_mplane *dfmt = &dq->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u32 blk_w = 0, blk_h = 0, mcu_cols = 0, mcu_rows = 0;
+ int rval;
+ u8 pln;
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_RESET_CMD, JPEG_V420_RST_CMD_SCALE_RESET_MSK, 1);
+
+ /* explicit no scaling */
+ jpeg_rw_bits(jenc, JPEG_V420_REG_SCALE_CFG, JPEG_V420_SCALE_CFG_HSCALE_ENABLE_MSK, 0);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_SCALE_CFG, JPEG_V420_SCALE_CFG_VSCALE_ENABLE_MSK, 0);
+
+ for (pln = 0; pln < QCOM_JPEG_MAX_PLANES; pln++) {
+ jpeg_io_write(jenc, jpeg_v420_scale_hstep[pln], JPEG_DEFAULT_SCALE_STEP);
+ jpeg_io_write(jenc, jpeg_v420_scale_vstep[pln], JPEG_DEFAULT_SCALE_STEP);
+ }
+
+ rval = jpeg_get_encode_fmt(sfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "unsupported encode format %p4cc\n",
+ &sfmt->pixelformat);
+ return -EINVAL;
+ }
+
+ rval = jpeg_get_mcu_geometry(rval, dfmt->width, dfmt->height, &blk_w, &blk_h,
+ &mcu_cols, &mcu_rows);
+ if (rval < 0) {
+ dev_err(ectx->dev, "invalid MCU geometry blk_w=%d blk_h=%d\n",
+ blk_w, blk_h);
+ return -EINVAL;
+ }
+
+ dev_dbg(ectx->dev, "scale blk_w=%u blk_h=%u cols=%u rows=%u\n",
+ blk_w, blk_h, mcu_cols, mcu_rows);
+
+ for (pln = 0; pln < sq->vf.num_planes; pln++) {
+ jpeg_rw_bits(jenc, jpeg_v420_scale_out_cfg[pln],
+ JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_MSK, blk_w - 1);
+ jpeg_rw_bits(jenc, jpeg_v420_scale_out_cfg[pln],
+ JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_MSK, blk_h - 1);
+ }
+
+ dev_dbg(ectx->dev, "scale src=%ux%u dst=%ux%u enable=%d/%d\n",
+ sfmt->width, sfmt->height, dfmt->width, dfmt->height,
+ jpeg_rd_bits(jenc, JPEG_V420_REG_SCALE_CFG, JPEG_V420_SCALE_CFG_HSCALE_ENABLE_MSK),
+ jpeg_rd_bits(jenc, JPEG_V420_REG_SCALE_CFG, JPEG_V420_SCALE_CFG_VSCALE_ENABLE_MSK));
+
+ /* Disabled, but must be configured */
+ jpeg_rw_bits(jenc, JPEG_V420_REG_CORE_CFG, JPEG_V420_CORE_CFG_SCALE_ENABLE_MSK, 0);
+
+ return 0;
+}
+
+static int jpeg_setup_encode(struct jenc_context *ectx)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ struct v4l2_pix_format_mplane *sfmt = &sq->vf;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u32 blk_w = 0, blk_h = 0, mcu_cols = 0, mcu_rows = 0;
+ int rval;
+
+ if (!sfmt->width || !sfmt->height)
+ return -EINVAL;
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_RESET_CMD, JPEG_V420_RST_CMD_ENCODER_RESET_MSK, 1);
+
+ rval = jpeg_get_encode_fmt(sfmt->pixelformat);
+ if (rval < 0) {
+ dev_err(ectx->dev, "unsupported encode format %p4cc\n",
+ &sfmt->pixelformat);
+ return -EINVAL;
+ }
+ jpeg_rw_bits(jenc, JPEG_V420_REG_ENC_CFG, JPEG_V420_ENC_CFG_IMAGE_FORMAT_MSK, rval);
+
+ rval = jpeg_get_mcu_geometry(rval, sfmt->width, sfmt->height, &blk_w, &blk_h,
+ &mcu_cols, &mcu_rows);
+ if (rval < 0) {
+ dev_err(ectx->dev, "invalid MCU geometry mcu_cols=%d mcu_rows=%d\n",
+ mcu_cols, mcu_rows);
+ return -EINVAL;
+ }
+
+ dev_dbg(ectx->dev, "encode blk_w=%u blk_h=%u cols=%u rows=%u\n",
+ blk_w, blk_h, mcu_cols, mcu_rows);
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_ENC_IMG_SIZE,
+ JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_MSK, mcu_cols - 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_ENC_IMG_SIZE,
+ JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_MSK, mcu_rows - 1);
+
+ dev_dbg(ectx->dev, "encode width=%d height=%d\n",
+ jpeg_rd_bits(jenc, JPEG_V420_REG_ENC_IMG_SIZE,
+ JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_MSK),
+ jpeg_rd_bits(jenc, JPEG_V420_REG_ENC_IMG_SIZE,
+ JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_MSK));
+
+ jpeg_rw_bits(jenc, JPEG_V420_REG_ENC_CFG, JPEG_V420_ENC_CFG_APPLY_EOI_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_CORE_CFG, JPEG_V420_CORE_CFG_ENC_ENABLE_MSK, 1);
+
+ return 0;
+}
+
+static irqreturn_t op_jpeg_irq_bot(int irq, void *data)
+{
+ struct qcom_jenc_dev *jenc = data;
+ unsigned long flags;
+ u32 irq_status;
+ u32 irq_mask;
+
+ irq_status = READ_ONCE(jenc->pending_irq_status);
+
+ irq_mask = JPEG_V420_IRQ_STATUS_SESSION_DONE_MSK;
+ if (jpeg_bits_get(irq_mask, irq_status)) {
+ struct jenc_context *ctx;
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ ctx = jenc->actx;
+ jenc->actx = NULL;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ if (ctx && ctx->bufq[JENC_DST_QUEUE].buff_id >= 0) {
+ struct qcom_jenc_queue *dq = &ctx->bufq[JENC_DST_QUEUE];
+ struct qcom_jpeg_buff *frame = &dq->buff[dq->buff_id];
+ size_t out_size = jpeg_rd_bits(jenc, JPEG_V420_REG_ENC_OUT_SIZE,
+ JPEG_V420_STATUS_OUT_SIZE_BYTES_MSK);
+
+ dev_dbg(jenc->dev, "complete idx:%d addr=%pad size=%zu\n",
+ dq->buff_id, &frame->plns[0].dma, out_size);
+
+ jenc->enc_hw_irq_cb(ctx, VB2_BUF_STATE_DONE,
+ out_size + ctx->hdr_cache.size);
+ }
+
+ jpeg_stop(jenc);
+ }
+
+ irq_mask = JPEG_V420_IRQ_STATUS_SESION_ERROR_MSK;
+ if (jpeg_bits_get(irq_mask, irq_status)) {
+ struct jenc_context *ctx;
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ ctx = jenc->actx;
+ jenc->actx = NULL;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ dev_err(jenc->dev, "encoder hardware failure=0x%x\n",
+ jpeg_bits_get(irq_mask, irq_status));
+ if (ctx)
+ jenc->enc_hw_irq_cb(ctx, VB2_BUF_STATE_ERROR, 0);
+
+ jpeg_stop(jenc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t op_jpeg_irq_top(int irq, void *data)
+{
+ struct qcom_jenc_dev *jenc = data;
+ unsigned long flags;
+ u32 irq_status;
+ u32 irq_mask;
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+
+ irq_status = jpeg_io_read(jenc, JPEG_V420_REG_INT_STATUS);
+ jpeg_wo_bits(jenc, JPEG_V420_REG_INT_CLR, JPEG_V420_IRQ_STATUS_ALL_BITS, irq_status);
+
+ irq_mask = JPEG_V420_IRQ_STATUS_RESET_ACK_MSK;
+ if (jpeg_bits_get(irq_mask, irq_status)) {
+ complete(&jenc->reset_complete);
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ irq_mask = JPEG_V420_IRQ_STATUS_STOP_ACK_MSK;
+ if (jpeg_bits_get(irq_mask, irq_status)) {
+ complete(&jenc->stop_complete);
+ dev_dbg(jenc->dev, "hardware stop acknowledged\n");
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ WRITE_ONCE(jenc->pending_irq_status, irq_status);
+
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void op_jpeg_get_hw_caps(struct qcom_jenc_dev *jenc, u32 *caps)
+{
+ u32 hw_caps;
+
+ hw_caps = jpeg_io_read(jenc, JPEG_V420_REG_HW_CAPABILITY);
+ dev_dbg(jenc->dev, "CAPS: encode=%d decode=%d upscale=%d downscale=%d\n",
+ jpeg_bits_get(JPEG_V420_HW_CAP_ENCODE_MSK, hw_caps),
+ jpeg_bits_get(JPEG_V420_HW_CAP_DECODE_MSK, hw_caps),
+ jpeg_bits_get(JPEG_V420_HW_CAP_UPSCALE_MSK, hw_caps),
+ jpeg_bits_get(JPEG_V420_HW_CAP_DOWNSCALE_MSK, hw_caps));
+
+ *caps = hw_caps;
+}
+
+static struct qcom_jenc_queue *op_jpeg_get_buff_queue(struct jenc_context *ectx,
+ enum qcom_enc_qid id)
+{
+ return &ectx->bufq[id];
+}
+
+static int op_jpeg_queue_setup(struct jenc_context *ectx, enum qcom_enc_qid id)
+{
+ int rc;
+
+ if (id == JENC_SRC_QUEUE) {
+ struct qcom_jenc_queue *q = &ectx->bufq[id];
+
+ rc = qcom_jenc_header_init(&ectx->hdr_cache, q->vf.pixelformat);
+ if (rc) {
+ dev_err(ectx->dev, "JFIF header init failed\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int op_jpeg_src_fmt_update(struct jenc_context *ectx, u32 old_fourcc, u32 new_fourcc)
+{
+ bool old_is_mono = (old_fourcc == V4L2_PIX_FMT_GREY);
+ bool new_is_mono = (new_fourcc == V4L2_PIX_FMT_GREY);
+ int rc;
+
+ /* header layout changes only for mono <-> color source format switch */
+ if (old_is_mono == new_is_mono)
+ return 0;
+
+ rc = qcom_jenc_header_init(&ectx->hdr_cache, new_fourcc);
+ if (rc) {
+ dev_err(ectx->dev, "JFIF header reinit failed\n");
+ return rc;
+ }
+
+ /* force DQT upload after source profile switch */
+ mutex_lock(&ectx->quality_mutex);
+ ectx->quality_programmed = 0;
+ mutex_unlock(&ectx->quality_mutex);
+
+ return 0;
+}
+
+static int op_jpeg_buffer_prepare(struct jenc_context *ectx, struct vb2_buffer *vb2)
+{
+ int rc;
+
+ if (V4L2_TYPE_IS_OUTPUT(vb2->type)) {
+ rc = jpeg_store_fe_next(ectx, vb2);
+ if (rc)
+ dev_err(ectx->dev, "cannot set up fetch engine address\n");
+ } else {
+ rc = jpeg_store_we_next(ectx, vb2);
+ if (rc)
+ dev_err(ectx->dev, "cannot set up write engine address\n");
+ }
+
+ return rc;
+}
+
+static int op_jpeg_process_exec(struct qcom_jenc_dev *jenc, struct jenc_context *ectx,
+ struct vb2_buffer *vb)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ struct qcom_jenc_queue *dq = &ectx->bufq[JENC_DST_QUEUE];
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ jenc->actx = ectx;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->type)) {
+ rc = jpeg_setup_fe(ectx, sq);
+ if (rc)
+ goto err_clear_ctx;
+
+ rc = jpeg_apply_fe_addr(ectx, sq, vb);
+ if (rc)
+ goto err_clear_ctx;
+ } else {
+ rc = jpeg_setup_we(ectx, dq);
+ if (rc)
+ goto err_clear_ctx;
+
+ rc = jpeg_apply_we_addr(ectx, dq, vb);
+ if (rc)
+ goto err_clear_ctx;
+ }
+
+ /* Both src and dst buffers queued for this frame — trigger encode. */
+ if (sq->sequence == dq->sequence) {
+ rc = jpeg_setup_scale(ectx);
+ if (rc)
+ goto err_clear_ctx;
+
+ rc = jpeg_setup_encode(ectx);
+ if (rc)
+ goto err_clear_ctx;
+
+ jpeg_exec(jenc);
+ }
+
+ return 0;
+
+err_clear_ctx:
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ if (jenc->actx == ectx)
+ jenc->actx = NULL;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ return rc;
+}
+
+static int op_jpeg_prepare(struct qcom_jenc_dev *jenc)
+{
+ jpeg_rw_bits(jenc, JPEG_V420_REG_RESET_CMD, JPEG_V420_RST_CMD_ENCODER_RESET_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_RESET_CMD, JPEG_V420_RST_CMD_BLOCK_FORMATTER_RST_MSK, 1);
+ jpeg_rw_bits(jenc, JPEG_V420_REG_RESET_CMD, JPEG_V420_RST_CMD_CORE_RESET_MSK, 1);
+
+ return 0;
+}
+
+static unsigned long jpeg_calc_optimal_clk(struct qcom_jenc_dev *jenc,
+ u32 width, u32 height, u32 fps)
+{
+ const struct qcom_dev_resources *res = jenc->res;
+ unsigned long ref_pixel_rate;
+ unsigned long optimal_clk;
+ unsigned long pixel_rate;
+
+ if (!width || !height || !fps)
+ return jenc->max_freq;
+
+ ref_pixel_rate = res->ref_throughput_mpps * 1000000UL;
+ pixel_rate = (unsigned long)width * height * fps;
+
+ optimal_clk = DIV_ROUND_UP_ULL(pixel_rate * res->ref_clk_hz, ref_pixel_rate);
+
+ return min_t(unsigned long, jenc->max_freq, optimal_clk);
+}
+
+static unsigned long jpeg_get_optimal_frequency(struct jenc_context *ectx)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ u32 fps;
+
+ /*
+ * If adaptive performance scaling is not requested always run at
+ * highest clock level for deterministic latency.
+ */
+ if (!v4l2_ctrl_g_ctrl(ectx->perf_level_auto_ctl))
+ return jenc->max_freq;
+
+ fps = (u32)v4l2_ctrl_g_ctrl(ectx->fps_target_ctl);
+ fps = clamp(fps, (u32)QCOM_JPEG_FPS_MIN, (u32)QCOM_JPEG_FPS_MAX);
+
+ return jpeg_calc_optimal_clk(jenc, sq->vf.width, sq->vf.height, fps);
+}
+
+static int op_jpeg_acquire(struct jenc_context *ectx, struct vb2_queue *q)
+{
+ struct qcom_jenc_queue *sq = &ectx->bufq[JENC_SRC_QUEUE];
+ struct qcom_jenc_queue *dq = &ectx->bufq[JENC_DST_QUEUE];
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ int rc;
+
+ /* reset per-context stream state for each (re)acquire */
+ sq->sequence = 0;
+ sq->buff_id = -1;
+ dq->sequence = 0;
+ dq->buff_id = -1;
+ /*
+ * Recreate JPEG header lazily per destination buffer to tolerate
+ * different valid V4L2 call orders (e.g. STREAMON before first QBUF).
+ */
+ ectx->hdr_cache.size = 0;
+ /* force DQT upload on first frame after (re)acquire */
+ mutex_lock(&ectx->quality_mutex);
+ ectx->quality_programmed = 0;
+ mutex_unlock(&ectx->quality_mutex);
+
+ if (atomic_inc_return(&jenc->ref_count) == 1) {
+ jenc->opt_freq = jpeg_get_optimal_frequency(ectx);
+
+ dev_dbg(jenc->dev, "freq=%lu src=%ux%u\n", jenc->opt_freq,
+ sq->vf.width, sq->vf.height);
+
+ rc = pm_runtime_resume_and_get(jenc->dev);
+ if (rc < 0) {
+ dev_err(jenc->dev, "PM runtime get failed\n");
+ atomic_dec(&jenc->ref_count);
+ return rc;
+ }
+
+ enable_irq(jenc->irq);
+
+ rc = jpeg_init(jenc);
+ if (rc) {
+ dev_err(jenc->dev, "hardware init failed\n");
+ disable_irq(jenc->irq);
+ atomic_dec(&jenc->ref_count);
+ pm_runtime_put_sync(jenc->dev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int op_jpeg_release(struct jenc_context *ectx, struct vb2_queue *q)
+{
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ int rc = 0;
+ int pm_rc;
+ int ref;
+
+ ref = atomic_dec_if_positive(&jenc->ref_count);
+ if (ref < 0) {
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+ if (!ref) {
+ rc = jpeg_deinit(jenc);
+ if (rc)
+ dev_err(jenc->dev, "hardware exit failed\n");
+
+ disable_irq(jenc->irq);
+
+ pm_rc = pm_runtime_put_sync(jenc->dev);
+ if (pm_rc < 0) {
+ dev_err(jenc->dev, "PM runtime put failed\n");
+ if (!rc)
+ rc = pm_rc;
+ }
+
+ dev_dbg(jenc->dev, "JPEG HW encoder released\n");
+ }
+
+ return rc;
+}
+
+const struct qcom_jpeg_hw_ops qcom_jpeg_default_ops = {
+ .hw_get_cap = op_jpeg_get_hw_caps,
+ .hw_acquire = op_jpeg_acquire,
+ .hw_release = op_jpeg_release,
+ .hw_prepare = op_jpeg_prepare,
+ .get_queue = op_jpeg_get_buff_queue,
+ .queue_setup = op_jpeg_queue_setup,
+ .src_fmt_update = op_jpeg_src_fmt_update,
+ .buf_prepare = op_jpeg_buffer_prepare,
+ .process_exec = op_jpeg_process_exec,
+ .hw_irq_top = op_jpeg_irq_top,
+ .hw_irq_bot = op_jpeg_irq_bot
+};
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h
new file mode 100644
index 000000000000..5a64e33a28b0
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_OPS_H
+#define QCOM_JENC_OPS_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <media/videobuf2-core.h>
+
+#include "qcom_jenc_dev.h"
+
+/*
+ * JENC encoder hardware operations.
+ */
+struct qcom_jpeg_hw_ops {
+ void (*hw_get_cap)
+ (struct qcom_jenc_dev *jenc_dev, u32 *hw_caps);
+
+ int (*hw_acquire)
+ (struct jenc_context *ectx, struct vb2_queue *queue);
+
+ int (*hw_release)
+ (struct jenc_context *ectx, struct vb2_queue *queue);
+
+ int (*hw_prepare)
+ (struct qcom_jenc_dev *jenc);
+
+ struct qcom_jenc_queue * (*get_queue)
+ (struct jenc_context *ectx, enum qcom_enc_qid id);
+
+ int (*queue_setup)
+ (struct jenc_context *ectx, enum qcom_enc_qid id);
+
+ int (*src_fmt_update)
+ (struct jenc_context *ectx, u32 old_fourcc, u32 new_fourcc);
+
+ int (*buf_prepare)
+ (struct jenc_context *ectx, struct vb2_buffer *vb2);
+
+ int (*process_exec)
+ (struct qcom_jenc_dev *jenc, struct jenc_context *ectx, struct vb2_buffer *vb2);
+
+ irqreturn_t (*hw_irq_top)(int irq_num, void *data);
+ irqreturn_t (*hw_irq_bot)(int irq_num, void *data);
+};
+
+extern const struct qcom_jpeg_hw_ops qcom_jpeg_default_ops;
+
+#endif /* QCOM_JENC_OPS_H */
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
new file mode 100644
index 000000000000..f9ab7c86792f
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include "qcom_jenc_ops.h"
+#include "qcom_jenc_res.h"
+
+#include "qcom_jenc_v420_hw_info.h"
+
+const struct qcom_dev_resources qcom_t165_t480_jpeg_drvdata = {
+ .hw_ops = &qcom_jpeg_default_ops,
+ .ref_clk_hz = 600000000UL,
+ .ref_throughput_mpps = 110UL,
+};
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
new file mode 100644
index 000000000000..91c6e789cffa
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_RES_H
+#define QCOM_JENC_RES_H
+
+#include "qcom_jenc_defs.h"
+
+struct qcom_dev_resources {
+ const struct qcom_jpeg_hw_ops *hw_ops;
+ unsigned long ref_clk_hz;
+ unsigned long ref_throughput_mpps;
+};
+
+extern const struct qcom_dev_resources qcom_t165_t480_jpeg_drvdata;
+
+#endif /* QCOM_JENC_RES_H */
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
new file mode 100644
index 000000000000..ebf69128cc2b
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
@@ -0,0 +1,410 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_V420_HW_INFO_H
+#define QCOM_JENC_V420_HW_INFO_H
+
+#include "qcom_jenc_defs.h"
+
+#define JPEG_V420_HW_VER_STEP_POS 0
+#define JPEG_V420_HW_VER_STEP_MSK \
+ (0xffffu << JPEG_V420_HW_VER_STEP_POS)
+
+#define JPEG_V420_HW_VER_MINOR_POS 16
+#define JPEG_V420_HW_VER_MINOR_MSK \
+ (0x0fffu << JPEG_V420_HW_VER_MINOR_POS)
+
+#define JPEG_V420_HW_VER_MAJOR_POS 28
+#define JPEG_V420_HW_VER_MAJOR_MSK \
+ (0xfu << JPEG_V420_HW_VER_MAJOR_POS)
+
+#define JPEG_V420_HW_CAP_ENCODE_MSK BIT_U32(0)
+#define JPEG_V420_HW_CAP_DECODE_MSK BIT_U32(1)
+
+#define JPEG_V420_HW_CAP_UPSCALE_POS 4
+#define JPEG_V420_HW_CAP_UPSCALE_MSK \
+ (0x7u << JPEG_V420_HW_CAP_UPSCALE_POS)
+
+#define JPEG_V420_HW_CAP_DOWNSCALE_POS 8
+#define JPEG_V420_HW_CAP_DOWNSCALE_MSK \
+ (0x7u << JPEG_V420_HW_CAP_DOWNSCALE_POS)
+
+#define JPEG_V420_RST_CMD_FE_RESET_MSK BIT_U32(0)
+#define JPEG_V420_RST_CMD_WE_RESET_MSK BIT_U32(1)
+#define JPEG_V420_RST_CMD_ENCODER_RESET_MSK BIT_U32(4)
+#define JPEG_V420_RST_CMD_DECODER_RESET_MSK BIT_U32(5)
+#define JPEG_V420_RST_CMD_BLOCK_FORMATTER_RST_MSK BIT_U32(6)
+#define JPEG_V420_RST_CMD_SCALE_RESET_MSK BIT_U32(7)
+#define JPEG_V420_RST_CMD_REGISTER_RESET_MSK BIT_U32(13)
+#define JPEG_V420_RST_CMD_MISR_RESET_MSK BIT_U32(16)
+#define JPEG_V420_RST_CMD_CORE_RESET_MSK BIT_U32(17)
+#define JPEG_V420_RST_CMD_JPEG_V420_DOMAIN_RESET_MSK BIT_U32(29)
+#define JPEG_V420_RST_CMD_RESET_BYPASS_MSK BIT_U32(31)
+
+#define JPEG_V420_CORE_CFG_FE_ENABLE_MSK BIT_U32(0)
+#define JPEG_V420_CORE_CFG_WE_ENABLE_MSK BIT_U32(1)
+#define JPEG_V420_CORE_CFG_ENC_ENABLE_MSK BIT_U32(4)
+#define JPEG_V420_CORE_CFG_SCALE_ENABLE_MSK BIT_U32(7)
+#define JPEG_V420_CORE_CFG_TESTBUS_ENABLE_MSK BIT_U32(19)
+#define JPEG_V420_CORE_CFG_MODE_MSK BIT_U32(24)
+#define JPEG_V420_CORE_CFG_CGC_DISABLE_MSK BIT_U32(31)
+
+#define JPEG_V420_CMD_HW_START_MSK BIT_U32(0)
+#define JPEG_V420_CMD_HW_STOP_MSK BIT_U32(1)
+#define JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK BIT_U32(4)
+#define JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK BIT_U32(5)
+#define JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK BIT_U32(6)
+#define JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK BIT_U32(8)
+#define JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK BIT_U32(9)
+#define JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK BIT_U32(10)
+#define JPEG_V420_CMD_APPLY_SWC_RD_PARAMS_MSK BIT_U32(11)
+
+#define JPEG_V420_CORE_STATE_STATUS_ENCODE_STATE_MSK BIT_U32(0)
+#define JPEG_V420_CORE_STATE_STATUS_SCALE_STATE_MSK BIT_U32(2)
+#define JPEG_V420_CORE_STATE_STATUS_REALTIME_STATE_MSK BIT_U32(4)
+#define JPEG_V420_CORE_STATE_STATUS_BUS_STATE_MSK BIT_U32(8)
+#define JPEG_V420_CORE_STATE_STATUS_CGC_STATE_MSK BIT_U32(9)
+
+#define JPEG_V420_FE_CFG_BYTE_ORDERING_POS 0
+#define JPEG_V420_FE_CFG_BYTE_ORDERING_MSK \
+ (0xfu << JPEG_V420_FE_CFG_BYTE_ORDERING_POS)
+
+#define JPEG_V420_FE_CFG_BURST_LENGTH_MAX_POS 4
+#define JPEG_V420_FE_CFG_BURST_LENGTH_MAX_MSK \
+ (0xfu << JPEG_V420_FE_CFG_BURST_LENGTH_MAX_POS)
+
+#define JPEG_V420_FE_CFG_MEMORY_FORMAT_POS 8
+#define JPEG_V420_FE_CFG_MEMORY_FORMAT_MSK \
+ (0x3u << JPEG_V420_FE_CFG_MEMORY_FORMAT_POS)
+
+#define JPEG_V420_FE_CFG_CBCR_ORDER_MSK BIT_U32(12)
+#define JPEG_V420_FE_CFG_BOTTOM_VPAD_EN_MSK BIT_U32(13)
+#define JPEG_V420_FE_CFG_PLN0_EN_MSK BIT_U32(16)
+#define JPEG_V420_FE_CFG_PLN1_EN_MSK BIT_U32(17)
+#define JPEG_V420_FE_CFG_PLN2_EN_MSK BIT_U32(18)
+#define JPEG_V420_FE_CFG_SIXTEEN_MCU_EN_MSK BIT_U32(21)
+#define JPEG_V420_FE_CFG_MCUS_PER_BLOCK_MSK BIT_U32(22)
+#define JPEG_V420_FE_CFG_MAL_BOUNDARY_MSK BIT_U32(24)
+#define JPEG_V420_FE_CFG_MAL_EN_MSK BIT_U32(27)
+
+#define JPEG_V420_PLN_RD_OFFS_OFFSET_POS 0
+#define JPEG_V420_PLN_RD_OFFS_OFFSET_MSK \
+ (0x1fffffffu << JPEG_V420_PLN_RD_OFFS_OFFSET_POS)
+
+#define JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_POS 0
+#define JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_MSK \
+ (0xffffu << JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_POS)
+
+#define JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_POS 16
+#define JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_MSK \
+ (0xffffu << JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_POS)
+
+#define JPEG_V420_PLN_RD_STRIDE_STRIDE_POS 0
+#define JPEG_V420_PLN_RD_STRIDE_STRIDE_MSK \
+ (0xffffu << JPEG_V420_PLN_RD_STRIDE_STRIDE_POS)
+
+#define JPEG_V420_PLN_RD_HINIT_FRACTIONAL_POS 0
+#define JPEG_V420_PLN_RD_HINIT_FRACTIONAL_MSK \
+ (0x1fffffu << JPEG_V420_PLN_RD_HINIT_FRACTIONAL_POS)
+
+#define JPEG_V420_PLN_RD_VINIT_FRACTIONAL_POS 0
+#define JPEG_V420_PLN_RD_VINIT_FRACTIONAL_MSK \
+ (0x1fffffu << JPEG_V420_PLN_RD_VINIT_FRACTIONAL_POS)
+
+#define JPEG_V420_WE_CFG_BYTE_ORDERING_POS 0
+#define JPEG_V420_WE_CFG_BYTE_ORDERING_MSK \
+ (0xfu << JPEG_V420_WE_CFG_BYTE_ORDERING_POS)
+
+#define JPEG_V420_WE_CFG_BURST_LENGTH_MAX_POS 4
+#define JPEG_V420_WE_CFG_BURST_LENGTH_MAX_MSK \
+ (0xfu << JPEG_V420_WE_CFG_BURST_LENGTH_MAX_POS)
+
+#define JPEG_V420_WE_CFG_MEMORY_FORMAT_POS 8
+#define JPEG_V420_WE_CFG_MEMORY_FORMAT_MSK \
+ (0x3u << JPEG_V420_WE_CFG_MEMORY_FORMAT_POS)
+
+#define JPEG_V420_WE_CFG_CBCR_ORDER_MSK BIT_U32(12)
+#define JPEG_V420_WE_CFG_PLN0_EN_MSK BIT_U32(16)
+#define JPEG_V420_WE_CFG_PLN1_EN_MSK BIT_U32(17)
+#define JPEG_V420_WE_CFG_PLN2_EN_MSK BIT_U32(18)
+#define JPEG_V420_WE_CFG_MAL_BOUNDARY_MSK BIT_U32(24)
+#define JPEG_V420_WE_CFG_MAL_EN_MSK BIT_U32(27)
+#define JPEG_V420_WE_CFG_POP_BUFF_ON_EOS_MSK BIT_U32(28)
+
+#define JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_POS 0
+#define JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_POS)
+
+#define JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_POS 16
+#define JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_POS)
+
+#define JPEG_V420_PLN_WR_STRIDE_STRIDE_POS 0
+#define JPEG_V420_PLN_WR_STRIDE_STRIDE_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_STRIDE_STRIDE_POS)
+
+#define JPEG_V420_PLN_WR_HINIT_INTEGER_POS 0
+#define JPEG_V420_PLN_WR_HINIT_INTEGER_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_HINIT_INTEGER_POS)
+
+#define JPEG_V420_PLN_WR_VINIT_INTEGER_POS 0
+#define JPEG_V420_PLN_WR_VINIT_INTEGER_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_VINIT_INTEGER_POS)
+
+#define JPEG_V420_PLN_WR_HSTEP_INTEGER_POS 0
+#define JPEG_V420_PLN_WR_HSTEP_INTEGER_MSK \
+ (0x1ffffu << JPEG_V420_PLN_WR_HSTEP_INTEGER_POS)
+
+#define JPEG_V420_PLN_WR_VSTEP_INTEGER_POS 0
+#define JPEG_V420_PLN_WR_VSTEP_INTEGER_MSK \
+ (0x1ffffu << JPEG_V420_PLN_WR_VSTEP_INTEGER_POS)
+
+#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_POS 0
+#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_POS)
+
+#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_POS 16
+#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_MSK \
+ (0xffffu << JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_POS)
+
+#define JPEG_V420_ENC_CFG_IMAGE_FORMAT_POS 0
+#define JPEG_V420_ENC_CFG_IMAGE_FORMAT_MSK \
+ (0x7u << JPEG_V420_ENC_CFG_IMAGE_FORMAT_POS)
+
+#define JPEG_V420_ENC_CFG_APPLY_EOI_MSK BIT_U32(7)
+#define JPEG_V420_ENC_CFG_HUFFMAN_SEL_MSK BIT_U32(8)
+#define JPEG_V420_ENC_CFG_FSC_ENABLE_MSK BIT_U32(11)
+#define JPEG_V420_ENC_CFG_OUTPUT_DISABLE_MSK BIT_U32(15)
+#define JPEG_V420_ENC_CFG_RST_MARKER_PERIOD_MSK BIT_U32(16)
+
+#define JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_POS 0u
+#define JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_MSK \
+ (0x1fffu << JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_POS)
+
+#define JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_POS 16u
+#define JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_MSK \
+ (0x1fffu << JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_POS)
+
+#define JPEG_V420_STATUS_OUT_SIZE_BYTES_POS 0
+#define JPEG_V420_STATUS_OUT_SIZE_BYTES_MSK \
+ (0x1fffffffu << JPEG_V420_STATUS_OUT_SIZE_BYTES_POS)
+
+#define JPEG_V420_SCALE_CFG_HSCALE_ENABLE_MSK BIT_U32(4)
+#define JPEG_V420_SCALE_CFG_VSCALE_ENABLE_MSK BIT_U32(5)
+#define JPEG_V420_SCALE_CFG_UPSAMPLE_EN_MSK BIT_U32(6)
+#define JPEG_V420_SCALE_CFG_SUBSAMPLE_EN_MSK BIT_U32(7)
+#define JPEG_V420_SCALE_CFG_HSCALE_ALGO_MSK BIT_U32(8)
+#define JPEG_V420_SCALE_CFG_VSCALE_ALGO_MSK BIT_U32(9)
+
+#define JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_POS 12u
+#define JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_MSK \
+ (0x3u << JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_POS)
+
+#define JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_POS 16u
+#define JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_MSK \
+ (0x3u << JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_POS)
+
+#define JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_POS 0
+#define JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_MSK \
+ (0xffu << JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_POS)
+
+#define JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_POS 16
+#define JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_MSK \
+ (0xfu << JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_POS)
+
+#define JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_POS 0
+#define JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_MSK \
+ (0x1fffffu << JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_POS)
+
+#define JPEG_V420_SCALE_PLN_HSTEP_INTEGER_POS 21
+#define JPEG_V420_SCALE_PLN_HSTEP_INTEGER_MSK \
+ (0x3fu << JPEG_V420_SCALE_PLN_HSTEP_INTEGER_POS)
+
+#define JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_POS 0
+#define JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_MSK \
+ (0x1fffffu << JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_POS)
+
+#define JPEG_V420_SCALE_PLN_VSTEP_INTEGER_POS 21
+#define JPEG_V420_SCALE_PLN_VSTEP_INTEGER_MSK \
+ (0x3fu << JPEG_V420_SCALE_PLN_VSTEP_INTEGER_POS)
+
+#define JPEG_V420_DMI_CFG_MEM_SEL_POS 0
+#define JPEG_V420_DMI_CFG_MEM_SEL_MSK \
+ (0x7u << JPEG_V420_DMI_CFG_MEM_SEL_POS)
+
+#define JPEG_V420_DMI_CFG_AUTO_INC_EN_MSK BIT_U32(4)
+
+/* DMI_CFG value to select quantization table memory and enable auto-increment */
+#define JPEG_DMI_ENABLE_UPLOAD (JPEG_V420_DMI_CFG_AUTO_INC_EN_MSK | 0x1u)
+
+#define JPEG_V420_DMI_ADDR_ADDR_POS 0
+#define JPEG_V420_DMI_ADDR_ADDR_MSK \
+ (0x3ffu << JPEG_V420_DMI_ADDR_ADDR_POS)
+
+#define JPEG_V420_TESTBUS_CFG_BUS_SEL_POS 0
+#define JPEG_V420_TESTBUS_CFG_BUS_SEL_MSK \
+ (0x3fu << JPEG_V420_TESTBUS_CFG_BUS_SEL_POS)
+
+#define JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_POS 0
+#define JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_MSK \
+ (0x1fffu << JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_POS)
+
+#define JPEG_V420_PLN_RD_HINIT_INT_INTEGER_POS 0
+#define JPEG_V420_PLN_RD_HINIT_INT_INTEGER_MSK \
+ (0x1ffffu << JPEG_V420_PLN_RD_HINIT_INT_INTEGER_POS)
+
+#define JPEG_V420_PLN_RD_VINIT_INT_INTEGER_POS 0
+#define JPEG_V420_PLN_RD_VINIT_INT_INTEGER_MSK \
+ (0x1ffffu << JPEG_V420_PLN_RD_VINIT_INT_INTEGER_POS)
+
+#define JPEG_V420_IRQ_STATUS_SESSION_DONE_MSK BIT_U32(0)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_DONE_MSK BIT_U32(4)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_DONE_MSK BIT_U32(5)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_DONE_MSK BIT_U32(6)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_REQ_ATTN_MSK BIT_U32(7)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_REQ_ATTN_MSK BIT_U32(8)
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_REQ_ATTN_MSK BIT_U32(9)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK BIT_U32(10)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_DONE_MSK BIT_U32(11)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_DONE_MSK BIT_U32(12)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_REQ_ATTN_MSK BIT_U32(13)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_REQ_ATTN_MSK BIT_U32(14)
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_REQ_ATTN_MSK BIT_U32(15)
+#define JPEG_V420_IRQ_STATUS_DCD_UNESCAPED_FF_MSK BIT_U32(19)
+#define JPEG_V420_IRQ_STATUS_DCD_HUFFMAN_ERROR_MSK BIT_U32(20)
+#define JPEG_V420_IRQ_STATUS_DCD_COEFF_ERROR_MSK BIT_U32(21)
+#define JPEG_V420_IRQ_STATUS_DCD_MISSING_BITSTUFF_MSK BIT_U32(22)
+#define JPEG_V420_IRQ_STATUS_DCD_SCAN_UNDERFLOW_MSK BIT_U32(23)
+#define JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_MSK BIT_U32(24)
+#define JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_SEQ_MSK BIT_U32(25)
+#define JPEG_V420_IRQ_STATUS_DCD_MISSING_RSM_MSK BIT_U32(26)
+#define JPEG_V420_IRQ_STATUS_STOP_ACK_MSK BIT_U32(27)
+#define JPEG_V420_IRQ_STATUS_RESET_ACK_MSK BIT_U32(28)
+
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_DONE_MSK \
+ (JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_DONE_MSK)
+
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_DONE_MSK \
+ (JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_DONE_MSK)
+
+#define JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_REQ_ATTN_MSK \
+ (JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_REQ_ATTN_MSK)
+
+#define JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_REQ_ATTN_MSK \
+ (JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_REQ_ATTN_MSK)
+
+#define JPEG_V420_IRQ_STATUS_SESION_ERROR_MSK \
+ (JPEG_V420_IRQ_STATUS_DCD_UNESCAPED_FF_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_HUFFMAN_ERROR_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_COEFF_ERROR_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_MISSING_BITSTUFF_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_SCAN_UNDERFLOW_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_SEQ_MSK | \
+ JPEG_V420_IRQ_STATUS_DCD_MISSING_RSM_MSK)
+
+#define JPEG_V420_IRQ_STATUS_ALL_BITS \
+ (JPEG_V420_IRQ_STATUS_SESSION_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK | \
+ JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_REQ_ATTN_MSK | \
+ JPEG_V420_IRQ_STATUS_SESION_ERROR_MSK | \
+ JPEG_V420_IRQ_STATUS_STOP_ACK_MSK | \
+ JPEG_V420_IRQ_STATUS_RESET_ACK_MSK)
+
+/* Register offsets for JPEG v4.2.0 hardware */
+#define JPEG_V420_REG_HW_VERSION 0x000
+#define JPEG_V420_REG_HW_CAPABILITY 0x004
+#define JPEG_V420_REG_RESET_CMD 0x008
+#define JPEG_V420_REG_CORE_CFG 0x00c
+#define JPEG_V420_REG_HW_CMD 0x010
+#define JPEG_V420_REG_ENC_CORE_STATE 0x014
+#define JPEG_V420_REG_INT_MASK 0x018
+#define JPEG_V420_REG_INT_CLR 0x01c
+#define JPEG_V420_REG_INT_STATUS 0x020
+#define JPEG_V420_REG_FE_CFG 0x024
+
+/* FE (fetch engine) plane registers */
+#define JPEG_V420_REG_FE_PNTR_0 0x038
+#define JPEG_V420_REG_FE_PNTR_1 0x044
+#define JPEG_V420_REG_FE_PNTR_2 0x050
+#define JPEG_V420_REG_FE_OFFS_0 0x03c
+#define JPEG_V420_REG_FE_OFFS_1 0x048
+#define JPEG_V420_REG_FE_OFFS_2 0x054
+#define JPEG_V420_REG_FE_PNTR_CNT 0x05c
+#define JPEG_V420_REG_FE_BSIZE_0 0x060
+#define JPEG_V420_REG_FE_BSIZE_1 0x068
+#define JPEG_V420_REG_FE_BSIZE_2 0x070
+#define JPEG_V420_REG_FE_STRIDE_0 0x064
+#define JPEG_V420_REG_FE_STRIDE_1 0x06c
+#define JPEG_V420_REG_FE_STRIDE_2 0x08c
+#define JPEG_V420_REG_FE_HINIT_0 0x074
+#define JPEG_V420_REG_FE_HINIT_1 0x078
+#define JPEG_V420_REG_FE_HINIT_2 0x07c
+#define JPEG_V420_REG_FE_VINIT_0 0x080
+#define JPEG_V420_REG_FE_VINIT_1 0x084
+#define JPEG_V420_REG_FE_VINIT_2 0x088
+#define JPEG_V420_REG_FE_VBPAD_CFG 0x2e8
+
+/* WE (write engine) plane registers */
+#define JPEG_V420_REG_WE_CFG 0x0c0
+#define JPEG_V420_REG_WE_PNTR_CNT 0x0e4
+#define JPEG_V420_REG_WE_PNTR_0 0x0cc
+#define JPEG_V420_REG_WE_PNTR_1 0x0d0
+#define JPEG_V420_REG_WE_PNTR_2 0x0d4
+#define JPEG_V420_REG_WE_BSIZE_0 0x0e8
+#define JPEG_V420_REG_WE_BSIZE_1 0x0ec
+#define JPEG_V420_REG_WE_BSIZE_2 0x0f0
+#define JPEG_V420_REG_WE_STRIDE_0 0x0f4
+#define JPEG_V420_REG_WE_STRIDE_1 0x0f8
+#define JPEG_V420_REG_WE_STRIDE_2 0x0fc
+#define JPEG_V420_REG_WE_HINIT_0 0x100
+#define JPEG_V420_REG_WE_HINIT_1 0x104
+#define JPEG_V420_REG_WE_HINIT_2 0x108
+#define JPEG_V420_REG_WE_VINIT_0 0x10c
+#define JPEG_V420_REG_WE_VINIT_1 0x110
+#define JPEG_V420_REG_WE_VINIT_2 0x114
+#define JPEG_V420_REG_WE_HSTEP_0 0x118
+#define JPEG_V420_REG_WE_HSTEP_1 0x11c
+#define JPEG_V420_REG_WE_HSTEP_2 0x120
+#define JPEG_V420_REG_WE_VSTEP_0 0x124
+#define JPEG_V420_REG_WE_VSTEP_1 0x128
+#define JPEG_V420_REG_WE_VSTEP_2 0x12c
+#define JPEG_V420_REG_WE_BLOCKS_0 0x130
+#define JPEG_V420_REG_WE_BLOCKS_1 0x134
+#define JPEG_V420_REG_WE_BLOCKS_2 0x138
+
+/* Encoder registers */
+#define JPEG_V420_REG_ENC_CFG 0x13c
+#define JPEG_V420_REG_ENC_IMG_SIZE 0x140
+#define JPEG_V420_REG_ENC_OUT_SIZE 0x180
+
+/* Scaler registers */
+#define JPEG_V420_REG_SCALE_CFG 0x26c
+#define JPEG_V420_REG_SCALE_OUT_CFG_0 0x270
+#define JPEG_V420_REG_SCALE_OUT_CFG_1 0x274
+#define JPEG_V420_REG_SCALE_OUT_CFG_2 0x278
+#define JPEG_V420_REG_SCALE_HSTEP_0 0x27c
+#define JPEG_V420_REG_SCALE_HSTEP_1 0x280
+#define JPEG_V420_REG_SCALE_HSTEP_2 0x284
+#define JPEG_V420_REG_SCALE_VSTEP_0 0x28c
+#define JPEG_V420_REG_SCALE_VSTEP_1 0x290
+#define JPEG_V420_REG_SCALE_VSTEP_2 0x294
+
+/* DMI registers */
+#define JPEG_V420_REG_DMI_CFG 0x298
+#define JPEG_V420_REG_DMI_ADDR 0x29c
+#define JPEG_V420_REG_DMI_DATA 0x2a0
+
+#endif /* QCOM_JENC_V420_HW_INFO_H */
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c
new file mode 100644
index 000000000000..aca96b454ea2
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c
@@ -0,0 +1,1154 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "qcom_jenc_defs.h"
+#include "qcom_jenc_dev.h"
+#include "qcom_jenc_ops.h"
+#include "qcom_jenc_v4l2.h"
+
+static const struct v4l2_frmsizeenum jpeg_def_frmsize = {
+ .stepwise = {
+ .min_width = QCOM_JPEG_HW_MIN_WIDTH,
+ .max_width = QCOM_JPEG_HW_MAX_WIDTH,
+ .step_width = QCOM_JPEG_HW_DEF_HSTEP,
+ .min_height = QCOM_JPEG_HW_MIN_HEIGHT,
+ .max_height = QCOM_JPEG_HW_MAX_HEIGHT,
+ .step_height = QCOM_JPEG_HW_DEF_VSTEP,
+ },
+ .type = V4L2_FRMSIZE_TYPE_STEPWISE
+};
+
+static const struct jenc_enc_format jpeg_src_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
+#define JPEG_SRC_FMT_COUNT ARRAY_SIZE(jpeg_src_formats)
+
+static const struct jenc_enc_format jpeg_dst_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }
+};
+
+#define JPEG_DST_FMT_COUNT ARRAY_SIZE(jpeg_dst_formats)
+
+static inline struct jenc_context *jpeg_file2ctx(struct file *file)
+{
+ return container_of(file_to_v4l2_fh(file), struct jenc_context, fh);
+}
+
+static struct qcom_jenc_queue *jpeg_get_bufq(struct jenc_context *ectx, enum qcom_enc_qid id)
+{
+ return &ectx->bufq[id];
+}
+
+static bool jpeg_v4l2_queues_busy(struct jenc_context *ctx)
+{
+ struct vb2_queue *out_q;
+ struct vb2_queue *cap_q;
+
+ out_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ cap_q = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ return vb2_is_busy(out_q) || vb2_is_busy(cap_q);
+}
+
+static bool jpeg_is_invalid_src(struct jenc_context *ectx, u32 type)
+{
+ bool is_invalid = (type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ if (is_invalid)
+ dev_dbg(ectx->dev, "invalid src type or format\n");
+
+ return is_invalid;
+}
+
+static bool jpeg_is_invalid_dst(struct jenc_context *ectx, u32 type)
+{
+ bool is_invalid = (type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ if (is_invalid)
+ dev_dbg(ectx->dev, "invalid dst type or format\n");
+
+ return is_invalid;
+}
+
+static const struct jenc_enc_format *jpeg_find_pix_format(enum qcom_enc_qid id, u32 fourcc)
+{
+ const struct jenc_enc_format *efmt;
+ unsigned int i, count;
+
+ if (id == JENC_SRC_QUEUE) {
+ count = JPEG_SRC_FMT_COUNT;
+ efmt = &jpeg_src_formats[0];
+ } else {
+ count = JPEG_DST_FMT_COUNT;
+ efmt = &jpeg_dst_formats[0];
+ }
+
+ for (i = 0; i < count; i++) {
+ if (efmt[i].fourcc == fourcc)
+ return &efmt[i];
+ }
+
+ return NULL;
+}
+
+static const struct jenc_enc_format *jpeg_get_format(struct jenc_context *ectx,
+ enum qcom_enc_qid qid, u32 pixelformat)
+{
+ const struct jenc_enc_format *efmt = jpeg_find_pix_format(qid, pixelformat);
+
+ if (!efmt)
+ efmt = (qid == JENC_SRC_QUEUE) ? &jpeg_src_formats[0] : &jpeg_dst_formats[0];
+
+ return efmt;
+}
+
+static int jpeg_update_src_planes(const struct jenc_enc_format *ef, struct v4l2_format *v4f)
+{
+ const struct v4l2_format_info *info = v4l2_format_info(ef->fourcc);
+ struct v4l2_pix_format_mplane *f = &v4f->fmt.pix_mp;
+ int rc;
+
+ if (!info)
+ return -EINVAL;
+
+ f->pixelformat = ef->fourcc;
+
+ f->field = V4L2_FIELD_NONE;
+ f->colorspace = V4L2_COLORSPACE_SRGB;
+ f->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(f->colorspace);
+ f->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(f->colorspace);
+ f->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(f->ycbcr_enc == V4L2_YCBCR_ENC_601,
+ f->colorspace, f->ycbcr_enc);
+
+ v4l2_apply_frmsize_constraints(&f->width, &f->height, &jpeg_def_frmsize.stepwise);
+
+ rc = v4l2_fill_pixfmt_mp(f, ef->fourcc, f->width, f->height);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static void jpeg_update_dst_plane(const struct jenc_enc_format *ef, struct v4l2_format *v4f)
+{
+ struct v4l2_pix_format_mplane *f = &v4f->fmt.pix_mp;
+ u32 nbx, nby;
+ u64 size;
+
+ f->pixelformat = ef->fourcc;
+ f->field = V4L2_FIELD_NONE;
+ f->colorspace = V4L2_COLORSPACE_SRGB;
+ f->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(f->colorspace);
+ f->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(f->colorspace);
+ f->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(f->ycbcr_enc == V4L2_YCBCR_ENC_601,
+ f->colorspace, f->ycbcr_enc);
+
+ v4l2_apply_frmsize_constraints(&f->width, &f->height, &jpeg_def_frmsize.stepwise);
+
+ /*
+ * JPEG is a variable-size format. The output size cannot be derived
+ * from bits per point or line stride.
+ *
+ * Provide a conservative upper bound based on worst-case entropy
+ * coding of 8x8 DCT blocks:
+ *
+ * - Each 8x8 block has 64 coefficients (1 DC + 63 AC).
+ * - In worst-case (high-entropy input, low quantization), all
+ * coefficients may be non-zero.
+ * - Huffman coding then emits (code + magnitude bits) per coefficient,
+ * which can approach ~2 bytes per coefficient in the worst case.
+ *
+ * => Worst-case is 64 coefficients * 2 bytes = 128 bytes per 8x8 block
+ * => approximately 2 bytes per point
+ *
+ * This bound implicitly covers byte stuffing (0xFF escaping) and is
+ * conservative with respect to subsampled formats (e.g. 4:2:0).
+ *
+ * Additional margin is added for headers and alignment.
+ *
+ * Note: This is a conservative upper bound, not an exact size.
+ */
+
+ nbx = DIV_ROUND_UP(f->width, 8);
+ nby = DIV_ROUND_UP(f->height, 8);
+
+ size = nbx * nby * 128;
+ size += JPEG_HEADER_MAX; /* JPEG header written by CPU before HW DMA */
+ size += SZ_4K; /* safety margin and alignment */
+
+ f->plane_fmt[0].bytesperline = 0;
+ f->plane_fmt[0].sizeimage = ALIGN(size, SZ_4K);
+}
+
+static int jpeg_enum_fmt_src(struct v4l2_fmtdesc *f)
+{
+ if (f->index >= JPEG_SRC_FMT_COUNT)
+ return -EINVAL;
+
+ f->pixelformat = jpeg_src_formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int jpeg_enum_fmt_dst(struct v4l2_fmtdesc *f)
+{
+ if (f->index >= JPEG_DST_FMT_COUNT)
+ return -EINVAL;
+
+ f->pixelformat = jpeg_dst_formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int jpeg_v4l2_try_format(struct jenc_context *ectx, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pm = &f->fmt.pix_mp;
+ const struct jenc_enc_format *ef;
+ int rc;
+
+ /* The function always returns valid driver format */
+ ef = jpeg_get_format(ectx, TYPE2QID(f->type), pm->pixelformat);
+
+ dev_dbg(ectx->dev, "type=%d %p4cc\n",
+ TYPE2QID(f->type), &ef->fourcc);
+
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
+ f->fmt.pix_mp.num_planes = 1;
+
+ jpeg_update_dst_plane(ef, f);
+
+ dev_dbg(ectx->dev, "\tImage: %dx%d Size:%9d\n", f->fmt.pix_mp.width,
+ f->fmt.pix_mp.height, f->fmt.pix_mp.plane_fmt[0].sizeimage);
+ } else {
+ const struct v4l2_format_info *info = v4l2_format_info(ef->fourcc);
+ u8 pln = 0;
+
+ if (!info)
+ return -EINVAL;
+
+ f->fmt.pix_mp.num_planes = info->comp_planes;
+
+ rc = jpeg_update_src_planes(ef, f);
+ if (rc)
+ return rc;
+
+ for (pln = 0; pln < f->fmt.pix_mp.num_planes; pln++)
+ dev_dbg(ectx->dev, "\tImage: %dx%d BPL:%5d Size:%9d\n",
+ pm->width, pm->height, pm->plane_fmt[pln].bytesperline,
+ pm->plane_fmt[pln].sizeimage);
+ }
+
+ return 0;
+}
+
+static int jpeg_v4l2_set_defaults(struct jenc_context *ectx)
+{
+ struct qcom_jenc_queue *sq = jpeg_get_bufq(ectx, JENC_SRC_QUEUE);
+ struct qcom_jenc_queue *dq = jpeg_get_bufq(ectx, JENC_DST_QUEUE);
+ struct v4l2_format f = {0};
+ int rc;
+
+ f.type = jpeg_src_formats->type;
+ f.fmt.pix_mp.pixelformat = jpeg_src_formats->fourcc;
+ f.fmt.pix_mp.width = QCOM_JPEG_HW_DEF_WIDTH;
+ f.fmt.pix_mp.height = QCOM_JPEG_HW_DEF_HEIGHT;
+
+ rc = jpeg_v4l2_try_format(ectx, &f);
+ if (rc)
+ return rc;
+
+ sq->vf = f.fmt.pix_mp;
+
+ f.type = jpeg_dst_formats->type;
+ f.fmt.pix_mp.pixelformat = jpeg_dst_formats->fourcc;
+ f.fmt.pix_mp.width = QCOM_JPEG_HW_DEF_WIDTH;
+ f.fmt.pix_mp.height = QCOM_JPEG_HW_DEF_HEIGHT;
+
+ rc = jpeg_v4l2_try_format(ectx, &f);
+ if (rc)
+ return rc;
+
+ dq->vf = f.fmt.pix_mp;
+
+ return 0;
+}
+
+static int jpeg_v4l2_set_format(struct jenc_context *ectx, struct v4l2_format *f)
+{
+ const struct qcom_jpeg_hw_ops *hw = ectx->jenc->res->hw_ops;
+ struct qcom_jenc_queue *q = jpeg_get_bufq(ectx, TYPE2QID(f->type));
+ struct qcom_jenc_queue *sq = jpeg_get_bufq(ectx, JENC_SRC_QUEUE);
+ struct v4l2_pix_format_mplane *pm = &f->fmt.pix_mp;
+ u32 old_src_fourcc = sq->vf.pixelformat;
+ int rc;
+
+ if (jpeg_v4l2_queues_busy(ectx))
+ return -EBUSY;
+
+ if (!v4l2_m2m_get_vq(ectx->fh.m2m_ctx, f->type)) {
+ dev_err(ectx->dev, "cannot get video queue\n");
+ return -EINVAL;
+ }
+
+ rc = jpeg_v4l2_try_format(ectx, f);
+ if (rc)
+ return rc;
+
+ /*
+ * Because scaling is not supported, source and destination image
+ * sizes must be equal.
+ */
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
+ /* Adjust source size to match capture size */
+ if (pm->width != sq->vf.width || pm->height != sq->vf.height) {
+ struct v4l2_format nf = {0};
+
+ nf.type = jpeg_src_formats->type;
+ nf.fmt.pix_mp.pixelformat = sq->vf.pixelformat;
+ nf.fmt.pix_mp.width = pm->width;
+ nf.fmt.pix_mp.height = pm->height;
+
+ rc = jpeg_v4l2_try_format(ectx, &nf);
+ if (rc)
+ return rc;
+
+ sq->vf = nf.fmt.pix_mp;
+ }
+
+ } else {
+ struct qcom_jenc_queue *dq = jpeg_get_bufq(ectx, JENC_DST_QUEUE);
+ struct v4l2_format nf = {0};
+
+ /* Adjust destination size to match source size */
+ if (pm->width != dq->vf.width || pm->height != dq->vf.height) {
+ nf.type = jpeg_dst_formats->type;
+ nf.fmt.pix_mp.pixelformat = dq->vf.pixelformat;
+ nf.fmt.pix_mp.width = pm->width;
+ nf.fmt.pix_mp.height = pm->height;
+
+ rc = jpeg_v4l2_try_format(ectx, &nf);
+ if (rc)
+ return rc;
+
+ dq->vf = nf.fmt.pix_mp;
+
+ /*
+ * The horizontal alignment of the destination is larger, and the
+ * result after adjustment may still differ. In this case, the
+ * requested image size should also be modified.
+ */
+ if (pm->width != nf.fmt.pix_mp.width ||
+ pm->height != nf.fmt.pix_mp.height) {
+ pm->width = nf.fmt.pix_mp.width;
+ pm->height = nf.fmt.pix_mp.height;
+ }
+ }
+ }
+
+ q->vf = *pm;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type) && hw->src_fmt_update) {
+ rc = hw->src_fmt_update(ectx, old_src_fourcc, q->vf.pixelformat);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static void jpeg_v4l2_get_format(struct jenc_context *ectx, struct v4l2_format *f)
+{
+ struct qcom_jenc_queue *q = jpeg_get_bufq(ectx, TYPE2QID(f->type));
+
+ f->fmt.pix_mp = q->vf;
+}
+
+static void jpeg_v4l2_work_stop(struct jenc_context *ctx, enum vb2_buffer_state buff_state);
+
+static void jpeg_finish_work(struct work_struct *work)
+{
+ struct jenc_context *ctx = container_of(work, struct jenc_context, finish_work);
+
+ v4l2_m2m_job_finish(ctx->jenc->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static void jpeg_stop_work(struct work_struct *work)
+{
+ struct jenc_context *ctx = container_of(work, struct jenc_context, stop_work);
+ struct qcom_jenc_dev *jenc = ctx->jenc;
+
+ mutex_lock(&jenc->dev_mutex);
+ jpeg_v4l2_work_stop(ctx, VB2_BUF_STATE_ERROR);
+ mutex_unlock(&jenc->dev_mutex);
+}
+
+static void jpeg_v4l2_work_done(struct jenc_context *ctx, size_t out_size)
+{
+ struct vb2_v4l2_buffer *vb;
+
+ vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ if (vb)
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE);
+
+ vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (vb) {
+ vb2_set_plane_payload(&vb->vb2_buf, 0, out_size);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ schedule_work(&ctx->finish_work);
+}
+
+static void jpeg_v4l2_work_stop(struct jenc_context *ctx, enum vb2_buffer_state buff_state)
+{
+ bool was_stopping = ctx->is_stopping;
+ struct vb2_v4l2_buffer *vb;
+
+ ctx->is_stopping = false;
+
+ /* Drain CAPTURE queue; signal EOS on last buffer if V4L2_ENC_CMD_STOP. */
+ while ((vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) {
+ if (was_stopping && !v4l2_m2m_last_dst_buf(ctx->fh.m2m_ctx)) {
+ vb2_set_plane_payload(&vb->vb2_buf, 0, 0);
+ v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vb);
+ } else {
+ v4l2_m2m_buf_done(vb, buff_state);
+ }
+ }
+
+ while ((vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
+ v4l2_m2m_buf_done(vb, buff_state);
+
+ /* Always call job_finish to let m2m schedule the next job. */
+ schedule_work(&ctx->finish_work);
+}
+
+static void jpeg_v4l2_process_cb(void *priv, enum vb2_buffer_state ev, size_t out_size)
+{
+ struct jenc_context *ctx = priv;
+ struct qcom_jenc_dev *jenc = ctx->jenc;
+
+ /* threaded IRQ path */
+ mutex_lock(&jenc->dev_mutex);
+
+ if (ev == VB2_BUF_STATE_DONE && out_size)
+ jpeg_v4l2_work_done(ctx, out_size);
+ else
+ jpeg_v4l2_work_stop(ctx, ev);
+
+ mutex_unlock(&jenc->dev_mutex);
+}
+
+static int jpeg_v4l2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct jenc_context *ectx = container_of(ctrl->handler, struct jenc_context, ctrl_hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ mutex_lock(&ectx->quality_mutex);
+ ectx->quality_requested = ctrl->val;
+ mutex_unlock(&ectx->quality_mutex);
+ break;
+ case V4L2_CID_QCOM_JPEG_PERF_LEVEL_AUTO:
+ /* value is read via v4l2_ctrl_g_ctrl() in jpeg_select_perf_level() */
+ break;
+ case V4L2_CID_QCOM_JPEG_FPS_TARGET:
+ /* value is read via v4l2_ctrl_g_ctrl() in jpeg_select_perf_level() */
+ break;
+ default:
+ dev_err(ectx->dev, "invalid control id=%#x\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops qcom_jpeg_v4l2_ctrl_ops = {
+ .s_ctrl = jpeg_v4l2_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config qcom_jpeg_perf_level_auto_cfg = {
+ .ops = &qcom_jpeg_v4l2_ctrl_ops,
+ .id = V4L2_CID_QCOM_JPEG_PERF_LEVEL_AUTO,
+ .name = "perf_level_auto",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config qcom_jpeg_fps_target_cfg = {
+ .ops = &qcom_jpeg_v4l2_ctrl_ops,
+ .id = V4L2_CID_QCOM_JPEG_FPS_TARGET,
+ .name = "fps_target",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = QCOM_JPEG_FPS_MIN,
+ .max = QCOM_JPEG_FPS_MAX,
+ .step = QCOM_JPEG_FPS_UNT,
+ .def = QCOM_JPEG_FPS_DEF,
+};
+
+static int bop_jpeg_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *plns_per_buff, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct jenc_context *ectx = vb2_get_drv_priv(vq);
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ const struct qcom_jpeg_hw_ops *hw = jenc->res->hw_ops;
+ struct qcom_jenc_queue *q;
+ int pln;
+
+ q = hw->get_queue(ectx, TYPE2QID(vq->type));
+ if (!q || !q->vf.num_planes)
+ return -EINVAL;
+
+ if (*plns_per_buff) {
+ if (*plns_per_buff != q->vf.num_planes)
+ return -EINVAL;
+
+ for (pln = 0; pln < q->vf.num_planes; ++pln) {
+ if (sizes[pln] < q->vf.plane_fmt[pln].sizeimage)
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ *plns_per_buff = q->vf.num_planes;
+ for (pln = 0; pln < q->vf.num_planes; ++pln) {
+ sizes[pln] = q->vf.plane_fmt[pln].sizeimage;
+ dev_dbg(ectx->dev, "queue=%d size[%d]=%d\n", TYPE2QID(vq->type),
+ pln, sizes[pln]);
+ }
+
+ return hw->queue_setup(ectx, TYPE2QID(vq->type));
+}
+
+static int bop_jpeg_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bop_jpeg_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct jenc_context *ectx = vb2_get_drv_priv(vb->vb2_queue);
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ const struct qcom_jpeg_hw_ops *hw = jenc->res->hw_ops;
+ struct qcom_jenc_queue *q = &ectx->bufq[TYPE2QID(vb->type)];
+ int pln;
+ int rc;
+
+ if (vb->num_planes != q->vf.num_planes)
+ return -EINVAL;
+
+ for (pln = 0; pln < q->vf.num_planes; pln++) {
+ if (q->vf.plane_fmt[pln].sizeimage == 0)
+ return -EINVAL;
+
+ if (vb2_plane_size(vb, pln) < q->vf.plane_fmt[pln].sizeimage)
+ return -EINVAL;
+ }
+
+ rc = hw->buf_prepare(ectx, vb);
+ if (rc) {
+ dev_err_ratelimited(ectx->dev, "buffer prepare failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static void bop_jpeg_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct jenc_context *ectx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ v4l2_m2m_buf_queue(ectx->fh.m2m_ctx, vbuf);
+}
+
+static int bop_jpeg_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct jenc_context *ectx = vb2_get_drv_priv(q);
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ const struct qcom_jpeg_hw_ops *hw = jenc->res->hw_ops;
+ struct qcom_jenc_queue *sq = jpeg_get_bufq(ectx, JENC_SRC_QUEUE);
+ struct qcom_jenc_queue *dq = jpeg_get_bufq(ectx, JENC_DST_QUEUE);
+ u32 hw_caps;
+ u8 pln;
+ int rc;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ dev_dbg(ectx->dev, "%p4cc %dx%d\n",
+ &sq->vf.pixelformat, sq->vf.width, sq->vf.height);
+
+ for (pln = 0; pln < sq->vf.num_planes; pln++) {
+ dev_dbg(ectx->dev, "\tpln=%d %dx%d bpl:%d size:%d\n", pln,
+ sq->vf.width, sq->vf.height,
+ sq->vf.plane_fmt[pln].bytesperline,
+ sq->vf.plane_fmt[pln].sizeimage);
+ }
+ } else {
+ dev_dbg(ectx->dev, "%p4cc %dx%d\n",
+ &dq->vf.pixelformat,
+ dq->vf.width, dq->vf.height);
+ }
+
+ mutex_lock(&jenc->dev_mutex);
+
+ /*
+ * Header cache is initialized lazily on CAPTURE QBUF, so valid V4L2
+ * orders like STREAMON before first QBUF still get a proper JPEG header.
+ */
+ if (!ectx->hw_acquired) {
+ rc = hw->hw_acquire(ectx, q);
+ if (!rc) {
+ ectx->hw_acquired = true;
+ hw->hw_get_cap(jenc, &hw_caps);
+ dev_dbg(ectx->dev, "hw_caps=0x%x\n", hw_caps);
+ }
+ } else {
+ rc = 0;
+ }
+
+ mutex_unlock(&jenc->dev_mutex);
+
+ return rc;
+}
+
+static void bop_jpeg_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct jenc_context *ectx = vb2_get_drv_priv(q);
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ const struct qcom_jpeg_hw_ops *hw = jenc->res->hw_ops;
+ bool do_release;
+
+ mutex_lock(&jenc->dev_mutex);
+
+ jpeg_v4l2_work_stop(ectx, VB2_BUF_STATE_ERROR);
+
+ do_release = ectx->hw_acquired;
+ ectx->hw_acquired = false;
+
+ mutex_unlock(&jenc->dev_mutex);
+
+ /*
+ * hw_release() calls jpeg_deinit() which issues HW_STOP and waits for
+ * the STOP_ACK interrupt completion. It must be called outside
+ * dev_mutex to avoid a deadlock: the threaded IRQ handler acquires
+ * dev_mutex before signalling the completion.
+ */
+ if (do_release)
+ hw->hw_release(ectx, q);
+}
+
+static const struct vb2_ops qcom_jpeg_v4l2_vb2_ops = {
+ .queue_setup = bop_jpeg_vb2_queue_setup,
+ .buf_out_validate = bop_jpeg_vb2_buf_out_validate,
+ .buf_prepare = bop_jpeg_vb2_buf_prepare,
+ .buf_queue = bop_jpeg_vb2_buf_queue,
+ .start_streaming = bop_jpeg_vb2_start_streaming,
+ .stop_streaming = bop_jpeg_vb2_stop_streaming,
+};
+
+static void mop_jpeg_m2m_job_abort(void *priv)
+{
+ struct jenc_context *ectx = priv;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+
+ mutex_lock(&jenc->dev_mutex);
+
+ jpeg_v4l2_work_stop(ectx, VB2_BUF_STATE_ERROR);
+
+ mutex_unlock(&jenc->dev_mutex);
+}
+
+static void mop_jpeg_m2m_job_run(void *priv)
+{
+ struct jenc_context *ectx = priv;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ const struct qcom_jpeg_hw_ops *hw = jenc->res->hw_ops;
+ struct vb2_v4l2_buffer *src_vb, *dst_vb;
+ struct qcom_jenc_queue *sq, *dq;
+
+ mutex_lock(&jenc->dev_mutex);
+
+ src_vb = v4l2_m2m_next_src_buf(ectx->fh.m2m_ctx);
+ dst_vb = v4l2_m2m_next_dst_buf(ectx->fh.m2m_ctx);
+
+ if (!src_vb || !dst_vb)
+ goto err_stop;
+
+ if (hw->hw_prepare(jenc))
+ goto err_stop;
+
+ sq = hw->get_queue(ectx, TYPE2QID(src_vb->vb2_buf.type));
+ src_vb->sequence = sq->sequence++;
+ if (hw->process_exec(jenc, ectx, &src_vb->vb2_buf))
+ goto err_stop;
+
+ dq = hw->get_queue(ectx, TYPE2QID(dst_vb->vb2_buf.type));
+ dst_vb->sequence = dq->sequence++;
+ if (hw->process_exec(jenc, ectx, &dst_vb->vb2_buf))
+ goto err_stop;
+
+ v4l2_m2m_buf_copy_metadata(src_vb, dst_vb);
+
+ mutex_unlock(&jenc->dev_mutex);
+ return;
+
+err_stop:
+ jpeg_v4l2_work_stop(ectx, VB2_BUF_STATE_ERROR);
+ mutex_unlock(&jenc->dev_mutex);
+}
+
+static const struct v4l2_m2m_ops qcom_jpeg_v4l2_m2m_ops = {
+ .device_run = mop_jpeg_m2m_job_run,
+ .job_abort = mop_jpeg_m2m_job_abort,
+};
+
+static int iop_jpeg_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, QCOM_JPEG_ENC_NAME, sizeof(cap->driver));
+ strscpy(cap->card, QCOM_JPEG_ENC_NAME, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", QCOM_JPEG_ENC_NAME);
+
+ return 0;
+}
+
+static int iop_jpeg_enum_fmt_vid_dst(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_dst(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_enum_fmt_dst(f);
+}
+
+static int iop_jpeg_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize)
+{
+ const struct jenc_enc_format *efmt;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ efmt = jpeg_find_pix_format(JENC_SRC_QUEUE, fsize->pixel_format);
+ if (efmt) {
+ fsize->type = jpeg_def_frmsize.type;
+ fsize->stepwise = jpeg_def_frmsize.stepwise;
+ return 0;
+ }
+
+ efmt = jpeg_find_pix_format(JENC_DST_QUEUE, fsize->pixel_format);
+ if (efmt) {
+ fsize->type = jpeg_def_frmsize.type;
+ fsize->stepwise = jpeg_def_frmsize.stepwise;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int iop_jpeg_enum_fmt_vid_src(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_src(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_enum_fmt_src(f);
+}
+
+static int iop_jpeg_get_fmt_vid_dst(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_dst(ectx, f->type))
+ return -EINVAL;
+
+ jpeg_v4l2_get_format(ectx, f);
+
+ return 0;
+}
+
+static int iop_jpeg_try_fmt_vid_dst(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_dst(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_v4l2_try_format(ectx, f);
+}
+
+static int iop_jpeg_set_fmt_vid_dst(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_dst(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_v4l2_set_format(ectx, f);
+}
+
+static int iop_jpeg_get_fmt_vid_src(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_src(ectx, f->type))
+ return -EINVAL;
+
+ jpeg_v4l2_get_format(ectx, f);
+
+ return 0;
+}
+
+static int iop_jpeg_try_fmt_vid_src(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_src(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_v4l2_try_format(ectx, f);
+}
+
+static int iop_jpeg_set_fmt_vid_src(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+
+ if (jpeg_is_invalid_src(ectx, f->type))
+ return -EINVAL;
+
+ return jpeg_v4l2_set_format(ectx, f);
+}
+
+static int iop_jpeg_encoder_command(struct file *file, void *priv, struct v4l2_encoder_cmd *ec)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+ struct vb2_queue *vq;
+ int rc;
+
+ if (ec->cmd == V4L2_ENC_CMD_STOP) {
+ vq = v4l2_m2m_get_src_vq(ectx->fh.m2m_ctx);
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ vq = v4l2_m2m_get_dst_vq(ectx->fh.m2m_ctx);
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ rc = v4l2_m2m_ioctl_encoder_cmd(file, priv, ec);
+ if (rc)
+ return rc;
+
+ ectx->is_stopping = true;
+ schedule_work(&ectx->stop_work);
+
+ return 0;
+ }
+
+ return v4l2_m2m_ioctl_encoder_cmd(file, priv, ec);
+}
+
+static const struct v4l2_ioctl_ops qcom_jpeg_v4l2_ioctl_ops = {
+ .vidioc_querycap = iop_jpeg_querycap,
+ .vidioc_enum_fmt_vid_cap = iop_jpeg_enum_fmt_vid_dst,
+ .vidioc_enum_fmt_vid_out = iop_jpeg_enum_fmt_vid_src,
+ .vidioc_enum_framesizes = iop_jpeg_enum_framesizes,
+
+ .vidioc_g_fmt_vid_cap_mplane = iop_jpeg_get_fmt_vid_dst,
+ .vidioc_try_fmt_vid_cap_mplane = iop_jpeg_try_fmt_vid_dst,
+ .vidioc_s_fmt_vid_cap_mplane = iop_jpeg_set_fmt_vid_dst,
+ .vidioc_g_fmt_vid_out_mplane = iop_jpeg_get_fmt_vid_src,
+ .vidioc_try_fmt_vid_out_mplane = iop_jpeg_try_fmt_vid_src,
+ .vidioc_s_fmt_vid_out_mplane = iop_jpeg_set_fmt_vid_src,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+
+ .vidioc_encoder_cmd = iop_jpeg_encoder_command,
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+};
+
+static int jpeg_v4l2_init_queue(void *priv, struct vb2_queue *sq, struct vb2_queue *dq)
+{
+ struct jenc_context *ectx = priv;
+ int rc;
+
+ sq->drv_priv = ectx;
+ sq->dev = ectx->dev;
+ sq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ sq->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
+ sq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ sq->ops = &qcom_jpeg_v4l2_vb2_ops;
+ sq->mem_ops = &vb2_dma_sg_memops;
+ sq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ sq->lock = &ectx->ctx_lock;
+ sq->min_queued_buffers = 1;
+
+ rc = vb2_queue_init(sq);
+ if (rc)
+ return rc;
+
+ dq->drv_priv = ectx;
+ dq->dev = ectx->dev;
+ dq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dq->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR;
+ dq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dq->ops = &qcom_jpeg_v4l2_vb2_ops;
+ dq->mem_ops = &vb2_dma_sg_memops;
+ dq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dq->lock = &ectx->ctx_lock;
+ dq->min_queued_buffers = 1;
+
+ rc = vb2_queue_init(dq);
+ if (rc) {
+ vb2_queue_release(sq);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int fop_jpeg_file_open(struct file *file)
+{
+ struct qcom_jenc_dev *jenc = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+ struct jenc_context *ectx;
+ int rc;
+
+ ectx = kzalloc_obj(*ectx, GFP_KERNEL);
+ if (!ectx)
+ return -ENOMEM;
+
+ ectx->dev = jenc->dev;
+ ectx->jenc = jenc;
+
+ /* default quality if userspace does not set the control explicitly */
+ ectx->quality_requested = QCOM_JPEG_QUALITY_DEF;
+ ectx->quality_programmed = 0;
+
+ mutex_init(&ectx->ctx_lock);
+ mutex_init(&ectx->quality_mutex);
+
+ INIT_WORK(&ectx->finish_work, jpeg_finish_work);
+ INIT_WORK(&ectx->stop_work, jpeg_stop_work);
+
+ rc = jpeg_v4l2_set_defaults(ectx);
+ if (rc)
+ goto err_unlock_free;
+
+ v4l2_fh_init(&ectx->fh, vdev);
+ v4l2_fh_add(&ectx->fh, file);
+
+ v4l2_ctrl_handler_init(&ectx->ctrl_hdl, 3);
+ ectx->quality_ctl = v4l2_ctrl_new_std(&ectx->ctrl_hdl,
+ &qcom_jpeg_v4l2_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QCOM_JPEG_QUALITY_MIN,
+ QCOM_JPEG_QUALITY_MAX,
+ QCOM_JPEG_QUALITY_UNT,
+ QCOM_JPEG_QUALITY_DEF);
+ ectx->perf_level_auto_ctl =
+ v4l2_ctrl_new_custom(&ectx->ctrl_hdl,
+ &qcom_jpeg_perf_level_auto_cfg, NULL);
+ ectx->fps_target_ctl =
+ v4l2_ctrl_new_custom(&ectx->ctrl_hdl,
+ &qcom_jpeg_fps_target_cfg, NULL);
+ if (ectx->ctrl_hdl.error) {
+ rc = ectx->ctrl_hdl.error;
+ goto err_fh_exit;
+ }
+
+ ectx->fh.ctrl_handler = &ectx->ctrl_hdl;
+
+ rc = v4l2_ctrl_handler_setup(&ectx->ctrl_hdl);
+ if (rc)
+ goto err_ctrl_handler_free;
+
+ v4l2_m2m_get(jenc->m2m_dev);
+ ectx->fh.m2m_ctx = v4l2_m2m_ctx_init(jenc->m2m_dev, ectx, &jpeg_v4l2_init_queue);
+ if (IS_ERR(ectx->fh.m2m_ctx)) {
+ rc = PTR_ERR(ectx->fh.m2m_ctx);
+ v4l2_m2m_put(jenc->m2m_dev);
+ goto err_ctrl_handler_free;
+ }
+
+ return 0;
+
+err_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&ectx->ctrl_hdl);
+err_fh_exit:
+ v4l2_fh_del(&ectx->fh, file);
+ v4l2_fh_exit(&ectx->fh);
+err_unlock_free:
+ mutex_destroy(&ectx->quality_mutex);
+ mutex_destroy(&ectx->ctx_lock);
+ kfree(ectx);
+
+ return rc;
+}
+
+static int fop_jpeg_file_release(struct file *file)
+{
+ struct jenc_context *ectx = jpeg_file2ctx(file);
+ struct v4l2_m2m_dev *m2m_dev = ectx->fh.m2m_ctx->m2m_dev;
+ struct qcom_jenc_dev *jenc = ectx->jenc;
+ unsigned long flags;
+
+ /*
+ * Ensure the threaded IRQ handler cannot dereference this context
+ * after it is freed. Clear jenc->actx under hw_lock before
+ * cancelling work and releasing the m2m context.
+ */
+ spin_lock_irqsave(&jenc->hw_lock, flags);
+ if (jenc->actx == ectx)
+ jenc->actx = NULL;
+ spin_unlock_irqrestore(&jenc->hw_lock, flags);
+
+ cancel_work_sync(&ectx->stop_work);
+ cancel_work_sync(&ectx->finish_work);
+
+ v4l2_m2m_ctx_release(ectx->fh.m2m_ctx);
+ v4l2_m2m_put(m2m_dev);
+ v4l2_ctrl_handler_free(&ectx->ctrl_hdl);
+ v4l2_fh_del(&ectx->fh, file);
+ v4l2_fh_exit(&ectx->fh);
+ mutex_destroy(&ectx->quality_mutex);
+ mutex_destroy(&ectx->ctx_lock);
+ kfree(ectx);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations qcom_jpeg_v4l2_file_ops = {
+ .owner = THIS_MODULE,
+ .open = fop_jpeg_file_open,
+ .release = fop_jpeg_file_release,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/* Release callback: free jenc after last fd is closed. */
+static void jenc_v4l2_dev_release(struct v4l2_device *v4l2_dev)
+{
+ struct qcom_jenc_dev *jenc = container_of(v4l2_dev, struct qcom_jenc_dev, v4l2_dev);
+
+ v4l2_device_unregister(&jenc->v4l2_dev);
+ v4l2_m2m_put(jenc->m2m_dev);
+ kfree(jenc);
+}
+
+int qcom_jpeg_v4l2_register(struct qcom_jenc_dev *jenc)
+{
+ int rc;
+
+ mutex_lock(&jenc->dev_mutex);
+
+ jenc->enc_hw_irq_cb = jpeg_v4l2_process_cb;
+
+ jenc->m2m_dev = v4l2_m2m_init(&qcom_jpeg_v4l2_m2m_ops);
+ if (IS_ERR(jenc->m2m_dev)) {
+ dev_err(jenc->dev, "failed to init mem2mem device\n");
+ rc = PTR_ERR(jenc->m2m_dev);
+ goto err_mutex_unlock;
+ }
+
+ jenc->vdev = video_device_alloc();
+ if (!jenc->vdev) {
+ rc = -ENOMEM;
+ goto err_m2m_release;
+ }
+
+ snprintf(jenc->vdev->name, sizeof(jenc->vdev->name), "%s", QCOM_JPEG_ENC_NAME);
+ jenc->vdev->fops = &qcom_jpeg_v4l2_file_ops;
+ jenc->vdev->ioctl_ops = &qcom_jpeg_v4l2_ioctl_ops;
+ jenc->vdev->minor = -1;
+ jenc->vdev->release = video_device_release;
+ jenc->vdev->lock = &jenc->dev_mutex;
+ jenc->vdev->v4l2_dev = &jenc->v4l2_dev;
+ jenc->vdev->vfl_dir = VFL_DIR_M2M;
+ jenc->vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
+
+ rc = video_register_device(jenc->vdev, VFL_TYPE_VIDEO, -1);
+ if (rc) {
+ dev_err(jenc->dev, "failed to register video device\n");
+ goto err_video_device_release;
+ }
+
+ video_set_drvdata(jenc->vdev, jenc);
+
+ jenc->v4l2_dev.release = jenc_v4l2_dev_release;
+
+ mutex_unlock(&jenc->dev_mutex);
+
+ dev_dbg(jenc->dev, "device registered as /dev/video%d\n", jenc->vdev->num);
+
+ return 0;
+
+err_video_device_release:
+ video_device_release(jenc->vdev);
+err_m2m_release:
+ v4l2_m2m_release(jenc->m2m_dev);
+err_mutex_unlock:
+ mutex_unlock(&jenc->dev_mutex);
+
+ return rc;
+}
+
diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h
new file mode 100644
index 000000000000..781b26dbf664
--- /dev/null
+++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef QCOM_JENC_V4L2_H
+#define QCOM_JENC_V4L2_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+struct qcom_jenc_dev;
+
+int qcom_jpeg_v4l2_register(struct qcom_jenc_dev *jenc);
+
+#endif /* QCOM_JENC_V4L2_H */
diff --git a/include/media/jpeg.h b/include/media/jpeg.h
index a01e142e99a7..926943a0d412 100644
--- a/include/media/jpeg.h
+++ b/include/media/jpeg.h
@@ -17,4 +17,37 @@
#define JPEG_MARKER_APP0 0xe0
#define JPEG_MARKER_COM 0xfe
+/* JPEG marker prefix byte */
+#define JPEG_MARKER_PREFIX 0xff
+
+/* JPEG baseline sample precision (bits per sample) */
+#define JPEG_SAMPLE_PRECISION_BITS 8
+
+/* Number of components: greyscale (1) and colour YCbCr (3) */
+#define JPEG_COMP_MONO 1
+#define JPEG_COMP_COLOR 3
+
+/* Chroma subsampling factors encoded in SOF0 sampling byte */
+#define JPEG_SAMPLING_H1V1 0x11
+#define JPEG_SAMPLING_H2V2 0x22
+
+/* Quantization table destination identifiers */
+#define JPEG_QTABLE_LUMA 0
+#define JPEG_QTABLE_CHROMA 1
+
+/* Huffman table class/destination identifiers for DC and AC tables */
+#define JPEG_DC_HT_INDEX_LUMA 0x00
+#define JPEG_DC_HT_INDEX_CHROMA 0x01
+#define JPEG_AC_HT_INDEX_LUMA 0x10
+#define JPEG_AC_HT_INDEX_CHROMA 0x11
+
+/* SOS spectral selection and approximation fields (baseline JPEG) */
+#define JPEG_SPECTRAL_START 0x00
+#define JPEG_SPECTRAL_END 0x3f
+#define JPEG_APPROX_HIGH_LOW 0x00
+
+/* SOS table selector bytes (luma: DC=0/AC=0, chroma: DC=1/AC=1) */
+#define JPEG_TABLE_SEL_LUMA 0x00
+#define JPEG_TABLE_SEL_CHROMA 0x11
+
#endif /* _MEDIA_JPEG_H_ */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index affec0ab4781..6408d3da29d4 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -234,6 +234,27 @@ enum v4l2_colorfx {
*/
#define V4L2_CID_USER_MALI_C55_BASE (V4L2_CID_USER_BASE + 0x1230)
+/* Qualcomm JPEG encoder controls */
+#define V4L2_CID_USER_QCOM_JENC_BASE (V4L2_CID_USER_BASE + 0x1240)
+
+/*
+ * V4L2_CID_QCOM_JPEG_PERF_LEVEL_AUTO - enable adaptive performance scaling.
+ *
+ * When set to 1 the driver selects the core clock OPP level based on the
+ * encoded frame resolution and fps target. When set to 0 (default) the
+ * driver always runs at NOMINAL (highest) OPP level.
+ */
+#define V4L2_CID_QCOM_JPEG_PERF_LEVEL_AUTO (V4L2_CID_USER_QCOM_JENC_BASE + 0)
+
+/*
+ * V4L2_CID_QCOM_JPEG_FPS_TARGET - target encode rate in frames per second.
+ *
+ * Used together with V4L2_CID_QCOM_JPEG_PERF_LEVEL_AUTO to select the lowest
+ * OPP level whose throughput is sufficient for the requested frame rate.
+ * Has no effect when perf_level_auto is 0. Range: 1-240, default: 30.
+ */
+#define V4L2_CID_QCOM_JPEG_FPS_TARGET (V4L2_CID_USER_QCOM_JENC_BASE + 1)
+
/* MPEG-class control IDs */
/* The MPEG controls are applicable to all codec controls
* and the 'MPEG' part of the define is historical */
--
2.34.1
^ permalink raw reply related [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
@ 2026-07-06 7:46 ` Vladimir Zapolskiy
2026-07-06 10:21 ` Bryan O'Donoghue
` (3 subsequent siblings)
4 siblings, 0 replies; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 7:46 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, loic.poulain, mchehab, robh, krzk+dt, conor+dt,
andersson, konradybcio, linux-arm-msm, linux-kernel, devicetree
Hi Atanas.
lt me provide a few initial simple review comments.
On 7/6/26 10:11, Atanas Filipov wrote:
> Add a Qualcomm JPEG encoder driver implemented on top of the
> V4L2 mem2mem framework.
>
> The driver wires vb2 queue handling, format negotiation, JPEG header
> handling, interrupt-driven job completion, and runtime PM/clock/ICC
> integration for the standalone JPEG encode hardware block.
>
> This series targets SM8250 (Kona) platforms.
>
> The jpeg-encoder node is described as a child node of the CAMSS block
> and is probed automatically via of_platform_populate() in camss_probe().
>
> Usage examples:
>
> - Check of related video node: v4l2-ctl --list-devices
> The expected result:
> qcom-jpeg-enc (platform:qcom-jpeg-enc):
> /dev/videoX
>
> V4L2 Examples:
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap
>
> v4l2-ctl -d /dev/video14 \
> -c compression_quality=100 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --stream-count=100
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --set-ctrl=perf_level_auto=1 \
> --set-ctrl=fps_target=30 \
> --stream-count=100
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=8192,height=8192,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --stream-count=1 \
> --stream-to=8192x8192_NM12.jpg
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=8192,height=8192,pixelformat=GREY \
> --stream-mmap --stream-out-mmap \
> --stream-count=1 \
> --stream-to=OUT_8192x8192.jpg
>
> v4l2-ctl -d /dev/video14 \
> -c compression_quality=100 \
> --set-fmt-video-out=width=1920,height=1088,pixelformat=NM12 \
> --stream-mmap=4 --stream-out-mmap=4 \
> --stream-count=100 \
> --stream-out-pattern=21 \
> --stream-to=OUT_1920x1088_100F.mjpg
>
> GStreamer examples:
>
> gst-launch-1.0 videotestsrc \
> is-live=false pattern=ball num-buffers=50 ! \
> video/x-raw,format=NV12,width=1920,height=1080 ! \
> videoscale ! videoconvert ! \
> video/x-raw,format=NV12,width=1920,height=1088 ! \
> v4l2jpegenc ! \
> fakesink sync=false
>
> gst-launch-1.0 videotestsrc is-live=true pattern=smpte ! \
> video/x-raw,format=NV12,width=1920,height=1088 ! \
> v4l2jpegenc extra-controls="controls,compression_quality=85" ! \
> fpsdisplaysink -v sync=false
>
> Performance measurements on SM8250
> (NV12, quality=100, GStreamer v4l2jpegenc vs jpegenc):
>
> +------------+-----------------------------+-------------------+
> | Resolution | HW v4l2jpegenc (q100) | SW jpegenc (q100) |
> | | Execution | avrFPS | maxFPS | Execution | FPS |
> +------------+-----------+--------+--------+-----------+-------+
> | 512x512 | 2.2332 | 1139.1 | 1195.4 | 7.1055 | 358.0 |
> | 1280x720 | 1.7300 | 418.5 | 457.2 | 5.0585 | 143.1 |
> | 1024x1024 | 1.6991 | 374.3 | 397.4 | 4.7304 | 134.4 |
> | 1920x1088 | 1.6596 | 192.8 | 193.8 | 3.7913 | 84.4 |
> | 2048x2048 | 1.7277 | 92.0 | 91.6 | 3.6343 | 43.7 |
> | 4096x4096 | 1.5887 | 25.2 | 25.3 | 4.2163 | 9.5 |
> | 8192x8192 | 1.3104 | 7.6 | 8.0 | 2.9987 | 3.3 |
> +------------+-----------+--------+--------+-----------+-------+
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../bindings/media/qcom,jpeg-encoder.yaml | 143 +-
> drivers/media/platform/qcom/Kconfig | 1 +
> drivers/media/platform/qcom/Makefile | 1 +
> drivers/media/platform/qcom/jpeg/Kconfig | 15 +
> drivers/media/platform/qcom/jpeg/Makefile | 9 +
> .../media/platform/qcom/jpeg/qcom_jenc_defs.h | 37 +
> .../media/platform/qcom/jpeg/qcom_jenc_dev.c | 314 ++++
> .../media/platform/qcom/jpeg/qcom_jenc_dev.h | 109 ++
> .../media/platform/qcom/jpeg/qcom_jenc_hdr.c | 331 ++++
> .../media/platform/qcom/jpeg/qcom_jenc_hdr.h | 119 ++
> .../media/platform/qcom/jpeg/qcom_jenc_ops.c | 1625 +++++++++++++++++
> .../media/platform/qcom/jpeg/qcom_jenc_ops.h | 52 +
> .../media/platform/qcom/jpeg/qcom_jenc_res.c | 15 +
> .../media/platform/qcom/jpeg/qcom_jenc_res.h | 19 +
> .../qcom/jpeg/qcom_jenc_v420_hw_info.h | 410 +++++
> .../media/platform/qcom/jpeg/qcom_jenc_v4l2.c | 1154 ++++++++++++
> .../media/platform/qcom/jpeg/qcom_jenc_v4l2.h | 23 +
> include/media/jpeg.h | 33 +
> include/uapi/linux/v4l2-controls.h | 21 +
> 19 files changed, 4357 insertions(+), 74 deletions(-)
> create mode 100644 drivers/media/platform/qcom/jpeg/Kconfig
> create mode 100644 drivers/media/platform/qcom/jpeg/Makefile
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_dev.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.c
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_ops.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.c
> create mode 100644 drivers/media/platform/qcom/jpeg/qcom_jenc_v4l2.h
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> index e4c16388ef07..53e83ebe4699 100644
> --- a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> @@ -72,80 +72,75 @@ examples:
> #include <dt-bindings/interconnect/qcom,sm8250.h>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
>
> - jpeg-encoder@ac53000 {
> - compatible = "qcom,sm8250-jenc";
> - reg = <0xac53000 0x1000>;
> -
> - interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
> -
> - clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
> - <&gcc GCC_CAMERA_SF_AXI_CLK>,
> - <&camcc CAM_CC_CORE_AHB_CLK>,
> - <&camcc CAM_CC_CPAS_AHB_CLK>,
> - <&camcc CAM_CC_CAMNOC_AXI_CLK>,
> - <&camcc CAM_CC_JPEG_CLK>;
> - clock-names = "hf_axi",
> - "sf_axi",
> - "core_ahb",
> - "cpas_ahb",
> - "cnoc_axi",
> - "jpeg";
> -
> - interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
> - &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
> - <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
> - &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
> - <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
> - &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
> - interconnect-names = "cpu-cfg",
> - "hf-mnoc",
> - "sf-mnoc";
> -
> - iommus = <&apps_smmu 0x2040 0x400>;
> -
> - operating-points-v2 = <&jpeg_opp_table>;
> -
> - jpeg_opp_table: opp-table {
> - compatible = "operating-points-v2";
> -
> - opp-100000000 {
> - opp-hz = /bits/ 64 <400000000>,
> - /bits/ 64 <100000000>;
> - opp-peak-kBps = <76800 104166 104166>;
> - opp-avg-kBps = <38400 33569 33569>;
> - required-opps = <&rpmhpd_opp_min_svs>;
> - };
> -
> - opp-200000000 {
> - opp-hz = /bits/ 64 <400000000>,
> - /bits/ 64 <200000000>;
> - opp-peak-kBps = <76800 208333 208333>;
> - opp-avg-kBps = <38400 67138 67138>;
> - required-opps = <&rpmhpd_opp_low_svs>;
> - };
> -
> - opp-400000000 {
> - opp-hz = /bits/ 64 <400000000>,
> - /bits/ 64 <400000000>;
> - opp-peak-kBps = <76800 416666 416666>;
> - opp-avg-kBps = <38400 134277 134277>;
> - required-opps = <&rpmhpd_opp_svs>;
> - };
> -
> - opp-480000000 {
> - opp-hz = /bits/ 64 <400000000>,
> - /bits/ 64 <480000000>;
> - opp-peak-kBps = <76800 500000 500000>;
> - opp-avg-kBps = <38400 161132 161132>;
> - required-opps = <&rpmhpd_opp_svs_l1>;
> - };
> -
> - opp-600000000 {
> - opp-hz = /bits/ 64 <400000000>,
> - /bits/ 64 <600000000>;
> - opp-peak-kBps = <76800 625000 625000>;
> - opp-avg-kBps = <38400 201416 201416>;
> - required-opps = <&rpmhpd_opp_nom>;
> + soc {
The 'jpeg-encoder' devicetree node is not placed directly under 'soc', which
invalidates this one change in the dt-bindings documentation.
In any case, this change shall be squashed with 3/5, and the doc file should
be named by compatible value, i.e. 'qcom,sm8250-jenc.yaml'.
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + jpeg-encoder@ac53000 {
> + compatible = "qcom,sm8250-jenc";
> + reg = <0 0x0ac53000 0 0x1000>;
> +
> + interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
> +
> + clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
> + <&gcc GCC_CAMERA_SF_AXI_CLK>,
> + <&camcc CAM_CC_CORE_AHB_CLK>,
> + <&camcc CAM_CC_CPAS_AHB_CLK>,
> + <&camcc CAM_CC_CAMNOC_AXI_CLK>,
> + <&camcc CAM_CC_JPEG_CLK>;
> + clock-names = "hf_axi",
> + "sf_axi",
> + "core_ahb",
> + "cpas_ahb",
> + "cnoc_axi",
> + "jpeg";
> +
> + interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
> + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
> + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
> + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
> + interconnect-names = "cpu-cfg",
> + "hf-mnoc",
> + "sf-mnoc";
This is the topic, which may raise a disagreement, but I'll repeat my
position about the need to remove all "CAMSS bus" specific resources from
the device node, they are found and should be allocated on parent's side.
From what I see it is applicable to all but one clock and all interconnects.
> +
> + iommus = <&apps_smmu 0x2040 0x400>;
> +
> + operating-points-v2 = <&jpeg_opp_table>;
> +
> + jpeg_opp_table: opp-table {
> + compatible = "operating-points-v2";
> +
> + opp-100000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <100000000>;
> + required-opps = <&rpmhpd_opp_min_svs>;
> + };
> +
> + opp-200000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <200000000>;
> + required-opps = <&rpmhpd_opp_low_svs>;
> + };
> +
> + opp-400000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <400000000>;
> + required-opps = <&rpmhpd_opp_svs>;
> + };
> +
> + opp-480000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <480000000>;
> + required-opps = <&rpmhpd_opp_svs_l1>;
> + };
> +
> + opp-600000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <600000000>;
> + required-opps = <&rpmhpd_opp_nom>;
> + };
> };
> };
> };
<snip>
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
> new file mode 100644
> index 000000000000..f9ab7c86792f
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.c
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include "qcom_jenc_ops.h"
> +#include "qcom_jenc_res.h"
> +
> +#include "qcom_jenc_v420_hw_info.h"
> +
> +const struct qcom_dev_resources qcom_t165_t480_jpeg_drvdata = {
> + .hw_ops = &qcom_jpeg_default_ops,
> + .ref_clk_hz = 600000000UL,
> + .ref_throughput_mpps = 110UL,
> +};
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
> new file mode 100644
> index 000000000000..91c6e789cffa
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_res.h
> @@ -0,0 +1,19 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef QCOM_JENC_RES_H
> +#define QCOM_JENC_RES_H
> +
> +#include "qcom_jenc_defs.h"
> +
> +struct qcom_dev_resources {
> + const struct qcom_jpeg_hw_ops *hw_ops;
> + unsigned long ref_clk_hz;
> + unsigned long ref_throughput_mpps;
> +};
> +
> +extern const struct qcom_dev_resources qcom_t165_t480_jpeg_drvdata;
> +
> +#endif /* QCOM_JENC_RES_H */
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
> new file mode 100644
> index 000000000000..ebf69128cc2b
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_v420_hw_info.h
> @@ -0,0 +1,410 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef QCOM_JENC_V420_HW_INFO_H
> +#define QCOM_JENC_V420_HW_INFO_H
> +
> +#include "qcom_jenc_defs.h"
> +
> +#define JPEG_V420_HW_VER_STEP_POS 0
> +#define JPEG_V420_HW_VER_STEP_MSK \
> + (0xffffu << JPEG_V420_HW_VER_STEP_POS)
> +
> +#define JPEG_V420_HW_VER_MINOR_POS 16
> +#define JPEG_V420_HW_VER_MINOR_MSK \
> + (0x0fffu << JPEG_V420_HW_VER_MINOR_POS)
> +
> +#define JPEG_V420_HW_VER_MAJOR_POS 28
> +#define JPEG_V420_HW_VER_MAJOR_MSK \
> + (0xfu << JPEG_V420_HW_VER_MAJOR_POS)
> +
> +#define JPEG_V420_HW_CAP_ENCODE_MSK BIT_U32(0)
> +#define JPEG_V420_HW_CAP_DECODE_MSK BIT_U32(1)
> +
> +#define JPEG_V420_HW_CAP_UPSCALE_POS 4
> +#define JPEG_V420_HW_CAP_UPSCALE_MSK \
> + (0x7u << JPEG_V420_HW_CAP_UPSCALE_POS)
> +
> +#define JPEG_V420_HW_CAP_DOWNSCALE_POS 8
> +#define JPEG_V420_HW_CAP_DOWNSCALE_MSK \
> + (0x7u << JPEG_V420_HW_CAP_DOWNSCALE_POS)
> +
> +#define JPEG_V420_RST_CMD_FE_RESET_MSK BIT_U32(0)
> +#define JPEG_V420_RST_CMD_WE_RESET_MSK BIT_U32(1)
> +#define JPEG_V420_RST_CMD_ENCODER_RESET_MSK BIT_U32(4)
> +#define JPEG_V420_RST_CMD_DECODER_RESET_MSK BIT_U32(5)
> +#define JPEG_V420_RST_CMD_BLOCK_FORMATTER_RST_MSK BIT_U32(6)
> +#define JPEG_V420_RST_CMD_SCALE_RESET_MSK BIT_U32(7)
> +#define JPEG_V420_RST_CMD_REGISTER_RESET_MSK BIT_U32(13)
> +#define JPEG_V420_RST_CMD_MISR_RESET_MSK BIT_U32(16)
> +#define JPEG_V420_RST_CMD_CORE_RESET_MSK BIT_U32(17)
> +#define JPEG_V420_RST_CMD_JPEG_V420_DOMAIN_RESET_MSK BIT_U32(29)
> +#define JPEG_V420_RST_CMD_RESET_BYPASS_MSK BIT_U32(31)
> +
> +#define JPEG_V420_CORE_CFG_FE_ENABLE_MSK BIT_U32(0)
> +#define JPEG_V420_CORE_CFG_WE_ENABLE_MSK BIT_U32(1)
> +#define JPEG_V420_CORE_CFG_ENC_ENABLE_MSK BIT_U32(4)
> +#define JPEG_V420_CORE_CFG_SCALE_ENABLE_MSK BIT_U32(7)
> +#define JPEG_V420_CORE_CFG_TESTBUS_ENABLE_MSK BIT_U32(19)
> +#define JPEG_V420_CORE_CFG_MODE_MSK BIT_U32(24)
> +#define JPEG_V420_CORE_CFG_CGC_DISABLE_MSK BIT_U32(31)
> +
> +#define JPEG_V420_CMD_HW_START_MSK BIT_U32(0)
> +#define JPEG_V420_CMD_HW_STOP_MSK BIT_U32(1)
> +#define JPEG_V420_CMD_CLR_RD_PLN0_QUEUE_MSK BIT_U32(4)
> +#define JPEG_V420_CMD_CLR_RD_PLN1_QUEUE_MSK BIT_U32(5)
> +#define JPEG_V420_CMD_CLR_RD_PLN2_QUEUE_MSK BIT_U32(6)
> +#define JPEG_V420_CMD_CLR_WR_PLN0_QUEUE_MSK BIT_U32(8)
> +#define JPEG_V420_CMD_CLR_WR_PLN1_QUEUE_MSK BIT_U32(9)
> +#define JPEG_V420_CMD_CLR_WR_PLN2_QUEUE_MSK BIT_U32(10)
> +#define JPEG_V420_CMD_APPLY_SWC_RD_PARAMS_MSK BIT_U32(11)
> +
> +#define JPEG_V420_CORE_STATE_STATUS_ENCODE_STATE_MSK BIT_U32(0)
> +#define JPEG_V420_CORE_STATE_STATUS_SCALE_STATE_MSK BIT_U32(2)
> +#define JPEG_V420_CORE_STATE_STATUS_REALTIME_STATE_MSK BIT_U32(4)
> +#define JPEG_V420_CORE_STATE_STATUS_BUS_STATE_MSK BIT_U32(8)
> +#define JPEG_V420_CORE_STATE_STATUS_CGC_STATE_MSK BIT_U32(9)
> +
> +#define JPEG_V420_FE_CFG_BYTE_ORDERING_POS 0
> +#define JPEG_V420_FE_CFG_BYTE_ORDERING_MSK \
> + (0xfu << JPEG_V420_FE_CFG_BYTE_ORDERING_POS)
> +
> +#define JPEG_V420_FE_CFG_BURST_LENGTH_MAX_POS 4
> +#define JPEG_V420_FE_CFG_BURST_LENGTH_MAX_MSK \
> + (0xfu << JPEG_V420_FE_CFG_BURST_LENGTH_MAX_POS)
> +
> +#define JPEG_V420_FE_CFG_MEMORY_FORMAT_POS 8
> +#define JPEG_V420_FE_CFG_MEMORY_FORMAT_MSK \
> + (0x3u << JPEG_V420_FE_CFG_MEMORY_FORMAT_POS)
> +
> +#define JPEG_V420_FE_CFG_CBCR_ORDER_MSK BIT_U32(12)
> +#define JPEG_V420_FE_CFG_BOTTOM_VPAD_EN_MSK BIT_U32(13)
> +#define JPEG_V420_FE_CFG_PLN0_EN_MSK BIT_U32(16)
> +#define JPEG_V420_FE_CFG_PLN1_EN_MSK BIT_U32(17)
> +#define JPEG_V420_FE_CFG_PLN2_EN_MSK BIT_U32(18)
> +#define JPEG_V420_FE_CFG_SIXTEEN_MCU_EN_MSK BIT_U32(21)
> +#define JPEG_V420_FE_CFG_MCUS_PER_BLOCK_MSK BIT_U32(22)
> +#define JPEG_V420_FE_CFG_MAL_BOUNDARY_MSK BIT_U32(24)
> +#define JPEG_V420_FE_CFG_MAL_EN_MSK BIT_U32(27)
> +
> +#define JPEG_V420_PLN_RD_OFFS_OFFSET_POS 0
> +#define JPEG_V420_PLN_RD_OFFS_OFFSET_MSK \
> + (0x1fffffffu << JPEG_V420_PLN_RD_OFFS_OFFSET_POS)
> +
> +#define JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_POS 0
> +#define JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_MSK \
> + (0xffffu << JPEG_V420_PLN_RD_BUFF_SIZE_WIDTH_POS)
> +
> +#define JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_POS 16
> +#define JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_MSK \
> + (0xffffu << JPEG_V420_PLN_RD_BUFF_SIZE_HEIGHT_POS)
> +
> +#define JPEG_V420_PLN_RD_STRIDE_STRIDE_POS 0
> +#define JPEG_V420_PLN_RD_STRIDE_STRIDE_MSK \
> + (0xffffu << JPEG_V420_PLN_RD_STRIDE_STRIDE_POS)
> +
> +#define JPEG_V420_PLN_RD_HINIT_FRACTIONAL_POS 0
> +#define JPEG_V420_PLN_RD_HINIT_FRACTIONAL_MSK \
> + (0x1fffffu << JPEG_V420_PLN_RD_HINIT_FRACTIONAL_POS)
> +
> +#define JPEG_V420_PLN_RD_VINIT_FRACTIONAL_POS 0
> +#define JPEG_V420_PLN_RD_VINIT_FRACTIONAL_MSK \
> + (0x1fffffu << JPEG_V420_PLN_RD_VINIT_FRACTIONAL_POS)
> +
> +#define JPEG_V420_WE_CFG_BYTE_ORDERING_POS 0
> +#define JPEG_V420_WE_CFG_BYTE_ORDERING_MSK \
> + (0xfu << JPEG_V420_WE_CFG_BYTE_ORDERING_POS)
> +
> +#define JPEG_V420_WE_CFG_BURST_LENGTH_MAX_POS 4
> +#define JPEG_V420_WE_CFG_BURST_LENGTH_MAX_MSK \
> + (0xfu << JPEG_V420_WE_CFG_BURST_LENGTH_MAX_POS)
> +
> +#define JPEG_V420_WE_CFG_MEMORY_FORMAT_POS 8
> +#define JPEG_V420_WE_CFG_MEMORY_FORMAT_MSK \
> + (0x3u << JPEG_V420_WE_CFG_MEMORY_FORMAT_POS)
> +
> +#define JPEG_V420_WE_CFG_CBCR_ORDER_MSK BIT_U32(12)
> +#define JPEG_V420_WE_CFG_PLN0_EN_MSK BIT_U32(16)
> +#define JPEG_V420_WE_CFG_PLN1_EN_MSK BIT_U32(17)
> +#define JPEG_V420_WE_CFG_PLN2_EN_MSK BIT_U32(18)
> +#define JPEG_V420_WE_CFG_MAL_BOUNDARY_MSK BIT_U32(24)
> +#define JPEG_V420_WE_CFG_MAL_EN_MSK BIT_U32(27)
> +#define JPEG_V420_WE_CFG_POP_BUFF_ON_EOS_MSK BIT_U32(28)
> +
> +#define JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_POS 0
> +#define JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_BUFF_SIZE_WIDTH_POS)
> +
> +#define JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_POS 16
> +#define JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_BUFF_SIZE_HEIGHT_POS)
> +
> +#define JPEG_V420_PLN_WR_STRIDE_STRIDE_POS 0
> +#define JPEG_V420_PLN_WR_STRIDE_STRIDE_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_STRIDE_STRIDE_POS)
> +
> +#define JPEG_V420_PLN_WR_HINIT_INTEGER_POS 0
> +#define JPEG_V420_PLN_WR_HINIT_INTEGER_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_HINIT_INTEGER_POS)
> +
> +#define JPEG_V420_PLN_WR_VINIT_INTEGER_POS 0
> +#define JPEG_V420_PLN_WR_VINIT_INTEGER_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_VINIT_INTEGER_POS)
> +
> +#define JPEG_V420_PLN_WR_HSTEP_INTEGER_POS 0
> +#define JPEG_V420_PLN_WR_HSTEP_INTEGER_MSK \
> + (0x1ffffu << JPEG_V420_PLN_WR_HSTEP_INTEGER_POS)
> +
> +#define JPEG_V420_PLN_WR_VSTEP_INTEGER_POS 0
> +#define JPEG_V420_PLN_WR_VSTEP_INTEGER_MSK \
> + (0x1ffffu << JPEG_V420_PLN_WR_VSTEP_INTEGER_POS)
> +
> +#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_POS 0
> +#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_COL_POS)
> +
> +#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_POS 16
> +#define JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_MSK \
> + (0xffffu << JPEG_V420_PLN_WR_BLK_CFG_BLOCKS_PER_ROW_POS)
> +
> +#define JPEG_V420_ENC_CFG_IMAGE_FORMAT_POS 0
> +#define JPEG_V420_ENC_CFG_IMAGE_FORMAT_MSK \
> + (0x7u << JPEG_V420_ENC_CFG_IMAGE_FORMAT_POS)
> +
> +#define JPEG_V420_ENC_CFG_APPLY_EOI_MSK BIT_U32(7)
> +#define JPEG_V420_ENC_CFG_HUFFMAN_SEL_MSK BIT_U32(8)
> +#define JPEG_V420_ENC_CFG_FSC_ENABLE_MSK BIT_U32(11)
> +#define JPEG_V420_ENC_CFG_OUTPUT_DISABLE_MSK BIT_U32(15)
> +#define JPEG_V420_ENC_CFG_RST_MARKER_PERIOD_MSK BIT_U32(16)
> +
> +#define JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_POS 0u
> +#define JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_MSK \
> + (0x1fffu << JPEG_V420_ENC_IMG_SIZE_ENCODE_WIDTH_POS)
> +
> +#define JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_POS 16u
> +#define JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_MSK \
> + (0x1fffu << JPEG_V420_ENC_IMG_SIZE_ENCODE_HEIGHT_POS)
> +
> +#define JPEG_V420_STATUS_OUT_SIZE_BYTES_POS 0
> +#define JPEG_V420_STATUS_OUT_SIZE_BYTES_MSK \
> + (0x1fffffffu << JPEG_V420_STATUS_OUT_SIZE_BYTES_POS)
> +
> +#define JPEG_V420_SCALE_CFG_HSCALE_ENABLE_MSK BIT_U32(4)
> +#define JPEG_V420_SCALE_CFG_VSCALE_ENABLE_MSK BIT_U32(5)
> +#define JPEG_V420_SCALE_CFG_UPSAMPLE_EN_MSK BIT_U32(6)
> +#define JPEG_V420_SCALE_CFG_SUBSAMPLE_EN_MSK BIT_U32(7)
> +#define JPEG_V420_SCALE_CFG_HSCALE_ALGO_MSK BIT_U32(8)
> +#define JPEG_V420_SCALE_CFG_VSCALE_ALGO_MSK BIT_U32(9)
> +
> +#define JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_POS 12u
> +#define JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_MSK \
> + (0x3u << JPEG_V420_SCALE_CFG_H_SCALE_FIR_ALGO_POS)
> +
> +#define JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_POS 16u
> +#define JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_MSK \
> + (0x3u << JPEG_V420_SCALE_CFG_V_SCALE_FIR_ALGO_POS)
> +
> +#define JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_POS 0
> +#define JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_MSK \
> + (0xffu << JPEG_V420_SCALE_OUT_CFG_BLOCK_WIDTH_POS)
> +
> +#define JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_POS 16
> +#define JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_MSK \
> + (0xfu << JPEG_V420_SCALE_OUT_CFG_BLOCK_HEIGHT_POS)
> +
> +#define JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_POS 0
> +#define JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_MSK \
> + (0x1fffffu << JPEG_V420_SCALE_PLN_HSTEP_FRACTIONAL_POS)
> +
> +#define JPEG_V420_SCALE_PLN_HSTEP_INTEGER_POS 21
> +#define JPEG_V420_SCALE_PLN_HSTEP_INTEGER_MSK \
> + (0x3fu << JPEG_V420_SCALE_PLN_HSTEP_INTEGER_POS)
> +
> +#define JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_POS 0
> +#define JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_MSK \
> + (0x1fffffu << JPEG_V420_SCALE_PLN_VSTEP_FRACTIONAL_POS)
> +
> +#define JPEG_V420_SCALE_PLN_VSTEP_INTEGER_POS 21
> +#define JPEG_V420_SCALE_PLN_VSTEP_INTEGER_MSK \
> + (0x3fu << JPEG_V420_SCALE_PLN_VSTEP_INTEGER_POS)
> +
> +#define JPEG_V420_DMI_CFG_MEM_SEL_POS 0
> +#define JPEG_V420_DMI_CFG_MEM_SEL_MSK \
> + (0x7u << JPEG_V420_DMI_CFG_MEM_SEL_POS)
> +
> +#define JPEG_V420_DMI_CFG_AUTO_INC_EN_MSK BIT_U32(4)
> +
> +/* DMI_CFG value to select quantization table memory and enable auto-increment */
> +#define JPEG_DMI_ENABLE_UPLOAD (JPEG_V420_DMI_CFG_AUTO_INC_EN_MSK | 0x1u)
> +
> +#define JPEG_V420_DMI_ADDR_ADDR_POS 0
> +#define JPEG_V420_DMI_ADDR_ADDR_MSK \
> + (0x3ffu << JPEG_V420_DMI_ADDR_ADDR_POS)
> +
> +#define JPEG_V420_TESTBUS_CFG_BUS_SEL_POS 0
> +#define JPEG_V420_TESTBUS_CFG_BUS_SEL_MSK \
> + (0x3fu << JPEG_V420_TESTBUS_CFG_BUS_SEL_POS)
> +
> +#define JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_POS 0
> +#define JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_MSK \
> + (0x1fffu << JPEG_V420_FE_VBPAD_CFG_BLOCK_ROW_POS)
> +
> +#define JPEG_V420_PLN_RD_HINIT_INT_INTEGER_POS 0
> +#define JPEG_V420_PLN_RD_HINIT_INT_INTEGER_MSK \
> + (0x1ffffu << JPEG_V420_PLN_RD_HINIT_INT_INTEGER_POS)
> +
> +#define JPEG_V420_PLN_RD_VINIT_INT_INTEGER_POS 0
> +#define JPEG_V420_PLN_RD_VINIT_INT_INTEGER_MSK \
> + (0x1ffffu << JPEG_V420_PLN_RD_VINIT_INT_INTEGER_POS)
> +
> +#define JPEG_V420_IRQ_STATUS_SESSION_DONE_MSK BIT_U32(0)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_DONE_MSK BIT_U32(4)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_DONE_MSK BIT_U32(5)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_DONE_MSK BIT_U32(6)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_REQ_ATTN_MSK BIT_U32(7)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_REQ_ATTN_MSK BIT_U32(8)
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_REQ_ATTN_MSK BIT_U32(9)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK BIT_U32(10)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_DONE_MSK BIT_U32(11)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_DONE_MSK BIT_U32(12)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_REQ_ATTN_MSK BIT_U32(13)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_REQ_ATTN_MSK BIT_U32(14)
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_REQ_ATTN_MSK BIT_U32(15)
> +#define JPEG_V420_IRQ_STATUS_DCD_UNESCAPED_FF_MSK BIT_U32(19)
> +#define JPEG_V420_IRQ_STATUS_DCD_HUFFMAN_ERROR_MSK BIT_U32(20)
> +#define JPEG_V420_IRQ_STATUS_DCD_COEFF_ERROR_MSK BIT_U32(21)
> +#define JPEG_V420_IRQ_STATUS_DCD_MISSING_BITSTUFF_MSK BIT_U32(22)
> +#define JPEG_V420_IRQ_STATUS_DCD_SCAN_UNDERFLOW_MSK BIT_U32(23)
> +#define JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_MSK BIT_U32(24)
> +#define JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_SEQ_MSK BIT_U32(25)
> +#define JPEG_V420_IRQ_STATUS_DCD_MISSING_RSM_MSK BIT_U32(26)
> +#define JPEG_V420_IRQ_STATUS_STOP_ACK_MSK BIT_U32(27)
> +#define JPEG_V420_IRQ_STATUS_RESET_ACK_MSK BIT_U32(28)
> +
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_DONE_MSK \
> + (JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_DONE_MSK)
> +
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_DONE_MSK \
> + (JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_DONE_MSK)
> +
> +#define JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_REQ_ATTN_MSK \
> + (JPEG_V420_IRQ_STATUS_RD_BUF_PLN0_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLN1_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLN2_REQ_ATTN_MSK)
> +
> +#define JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_REQ_ATTN_MSK \
> + (JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLN1_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLN2_REQ_ATTN_MSK)
> +
> +#define JPEG_V420_IRQ_STATUS_SESION_ERROR_MSK \
> + (JPEG_V420_IRQ_STATUS_DCD_UNESCAPED_FF_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_HUFFMAN_ERROR_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_COEFF_ERROR_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_MISSING_BITSTUFF_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_SCAN_UNDERFLOW_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_INVALID_RSM_SEQ_MSK | \
> + JPEG_V420_IRQ_STATUS_DCD_MISSING_RSM_MSK)
> +
> +#define JPEG_V420_IRQ_STATUS_ALL_BITS \
> + (JPEG_V420_IRQ_STATUS_SESSION_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLN0_DONE_MSK | \
> + JPEG_V420_IRQ_STATUS_RD_BUF_PLNS_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_WR_BUF_PLNS_REQ_ATTN_MSK | \
> + JPEG_V420_IRQ_STATUS_SESION_ERROR_MSK | \
> + JPEG_V420_IRQ_STATUS_STOP_ACK_MSK | \
> + JPEG_V420_IRQ_STATUS_RESET_ACK_MSK)
> +
> +/* Register offsets for JPEG v4.2.0 hardware */
> +#define JPEG_V420_REG_HW_VERSION 0x000
> +#define JPEG_V420_REG_HW_CAPABILITY 0x004
> +#define JPEG_V420_REG_RESET_CMD 0x008
> +#define JPEG_V420_REG_CORE_CFG 0x00c
> +#define JPEG_V420_REG_HW_CMD 0x010
> +#define JPEG_V420_REG_ENC_CORE_STATE 0x014
> +#define JPEG_V420_REG_INT_MASK 0x018
> +#define JPEG_V420_REG_INT_CLR 0x01c
> +#define JPEG_V420_REG_INT_STATUS 0x020
> +#define JPEG_V420_REG_FE_CFG 0x024
> +
> +/* FE (fetch engine) plane registers */
> +#define JPEG_V420_REG_FE_PNTR_0 0x038
> +#define JPEG_V420_REG_FE_PNTR_1 0x044
> +#define JPEG_V420_REG_FE_PNTR_2 0x050
> +#define JPEG_V420_REG_FE_OFFS_0 0x03c
> +#define JPEG_V420_REG_FE_OFFS_1 0x048
> +#define JPEG_V420_REG_FE_OFFS_2 0x054
> +#define JPEG_V420_REG_FE_PNTR_CNT 0x05c
> +#define JPEG_V420_REG_FE_BSIZE_0 0x060
> +#define JPEG_V420_REG_FE_BSIZE_1 0x068
> +#define JPEG_V420_REG_FE_BSIZE_2 0x070
> +#define JPEG_V420_REG_FE_STRIDE_0 0x064
> +#define JPEG_V420_REG_FE_STRIDE_1 0x06c
> +#define JPEG_V420_REG_FE_STRIDE_2 0x08c
> +#define JPEG_V420_REG_FE_HINIT_0 0x074
> +#define JPEG_V420_REG_FE_HINIT_1 0x078
> +#define JPEG_V420_REG_FE_HINIT_2 0x07c
> +#define JPEG_V420_REG_FE_VINIT_0 0x080
> +#define JPEG_V420_REG_FE_VINIT_1 0x084
> +#define JPEG_V420_REG_FE_VINIT_2 0x088
> +#define JPEG_V420_REG_FE_VBPAD_CFG 0x2e8
> +
> +/* WE (write engine) plane registers */
> +#define JPEG_V420_REG_WE_CFG 0x0c0
> +#define JPEG_V420_REG_WE_PNTR_CNT 0x0e4
> +#define JPEG_V420_REG_WE_PNTR_0 0x0cc
> +#define JPEG_V420_REG_WE_PNTR_1 0x0d0
> +#define JPEG_V420_REG_WE_PNTR_2 0x0d4
> +#define JPEG_V420_REG_WE_BSIZE_0 0x0e8
> +#define JPEG_V420_REG_WE_BSIZE_1 0x0ec
> +#define JPEG_V420_REG_WE_BSIZE_2 0x0f0
> +#define JPEG_V420_REG_WE_STRIDE_0 0x0f4
> +#define JPEG_V420_REG_WE_STRIDE_1 0x0f8
> +#define JPEG_V420_REG_WE_STRIDE_2 0x0fc
> +#define JPEG_V420_REG_WE_HINIT_0 0x100
> +#define JPEG_V420_REG_WE_HINIT_1 0x104
> +#define JPEG_V420_REG_WE_HINIT_2 0x108
> +#define JPEG_V420_REG_WE_VINIT_0 0x10c
> +#define JPEG_V420_REG_WE_VINIT_1 0x110
> +#define JPEG_V420_REG_WE_VINIT_2 0x114
> +#define JPEG_V420_REG_WE_HSTEP_0 0x118
> +#define JPEG_V420_REG_WE_HSTEP_1 0x11c
> +#define JPEG_V420_REG_WE_HSTEP_2 0x120
> +#define JPEG_V420_REG_WE_VSTEP_0 0x124
> +#define JPEG_V420_REG_WE_VSTEP_1 0x128
> +#define JPEG_V420_REG_WE_VSTEP_2 0x12c
> +#define JPEG_V420_REG_WE_BLOCKS_0 0x130
> +#define JPEG_V420_REG_WE_BLOCKS_1 0x134
> +#define JPEG_V420_REG_WE_BLOCKS_2 0x138
> +
> +/* Encoder registers */
> +#define JPEG_V420_REG_ENC_CFG 0x13c
> +#define JPEG_V420_REG_ENC_IMG_SIZE 0x140
> +#define JPEG_V420_REG_ENC_OUT_SIZE 0x180
> +
> +/* Scaler registers */
> +#define JPEG_V420_REG_SCALE_CFG 0x26c
> +#define JPEG_V420_REG_SCALE_OUT_CFG_0 0x270
> +#define JPEG_V420_REG_SCALE_OUT_CFG_1 0x274
> +#define JPEG_V420_REG_SCALE_OUT_CFG_2 0x278
> +#define JPEG_V420_REG_SCALE_HSTEP_0 0x27c
> +#define JPEG_V420_REG_SCALE_HSTEP_1 0x280
> +#define JPEG_V420_REG_SCALE_HSTEP_2 0x284
> +#define JPEG_V420_REG_SCALE_VSTEP_0 0x28c
> +#define JPEG_V420_REG_SCALE_VSTEP_1 0x290
> +#define JPEG_V420_REG_SCALE_VSTEP_2 0x294
> +
> +/* DMI registers */
> +#define JPEG_V420_REG_DMI_CFG 0x298
> +#define JPEG_V420_REG_DMI_ADDR 0x29c
> +#define JPEG_V420_REG_DMI_DATA 0x2a0
> +
> +#endif /* QCOM_JENC_V420_HW_INFO_H */
qcom_jenc_v420_hw_info.h shall be removed, and its content goes to
qcom_jenc_res.c file, no info is shared from the header, and static
declarations in header files are not favoured.
In general the driver loos good.
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node
2026-07-06 7:11 ` [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node Atanas Filipov
@ 2026-07-06 10:07 ` Bryan O'Donoghue
2026-07-06 12:11 ` Krzysztof Kozlowski
1 sibling, 0 replies; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 10:07 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 08:11, Atanas Filipov wrote:
> The CAMSS node uses #address-cells = <2>, #size-cells = <2> and ranges
> to act as a bus for child IP blocks such as the JPEG encoder. Add these
> properties to the binding so that dtbs_check does not reject them.
>
> Use unevaluatedProperties: false instead of additionalProperties: false
> so that child nodes are validated against their own schema (matched by
> compatible) without requiring the parent binding to enumerate each child
> type explicitly. This is the correct approach for a bus-like container
> node and avoids updating the camss binding for every new child device
> added in the future.
>
> No functional change.
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../devicetree/bindings/media/qcom,sm8250-camss.yaml | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> index a509d4bbcb4a..6008938ee369 100644
> --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> @@ -102,6 +102,14 @@ properties:
> - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
> - description: Titan GDSC - Titan ISP Block, Global Distributed Switch Controller.
>
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 2
> +
> + ranges: true
> +
> ports:
> $ref: /schemas/graph.yaml#/properties/ports
>
> @@ -317,7 +325,7 @@ required:
> - vdda-phy-supply
> - vdda-pll-supply
>
> -additionalProperties: false
> +unevaluatedProperties: false
>
> examples:
> - |
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
` (4 preceding siblings ...)
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
@ 2026-07-06 10:12 ` Bryan O'Donoghue
2026-07-06 12:00 ` Vladimir Zapolskiy
2026-07-06 12:24 ` Krzysztof Kozlowski
6 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 10:12 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 08:11, Atanas Filipov wrote:
> Note: The handling of shared camera subsystem resources (power domains,
> interconnects) for child IP blocks is still an open design question.
Why ?
A device needs to vote on its own interconnect and power-domains on any
bus. A sub-device of another device may wish to ramp a clock for
whatever reason.
There is no "master" device in this block of devices - save perhaps for
the CSID mux / wrappers on some of these parts.
We have shared resources like camera noc, system noc and external clocks.
Please include power-domains and interconnects.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding
2026-07-06 7:11 ` [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding Atanas Filipov
@ 2026-07-06 10:19 ` Bryan O'Donoghue
2026-07-06 12:17 ` Krzysztof Kozlowski
1 sibling, 0 replies; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 10:19 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 08:11, Atanas Filipov wrote:
> Add device-tree binding for the Qualcomm JPEG encoder hardware block
> present in SM8250 (Kona) SoCs.
>
> The JPEG encoder is a standalone hardware IP within the camera subsystem
> that performs JPEG compression in memory-to-memory fashion. It is
> separate from the CAMSS ISP pipeline and has its own register space,
> interrupt, clocks, power domain, IOMMU streams, and interconnect paths.
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../bindings/media/qcom,jpeg-encoder.yaml | 151 ++++++++++++++++++
> 1 file changed, 151 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> new file mode 100644
> index 000000000000..e4c16388ef07
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> @@ -0,0 +1,151 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/qcom,jpeg-encoder.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm JPEG Encoder
> +
> +maintainers:
> + - Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> +
> +description:
> + Qualcomm JPEG Encoder is the JPEG encode hardware present in Qualcomm SoCs.
> +
> +properties:
> + compatible:
> + enum:
> + - qcom,sm8250-jenc
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 6
> +
> + clock-names:
> + items:
> + - const: hf_axi
> + - const: sf_axi
> + - const: core_ahb
> + - const: cpas_ahb
> + - const: cnoc_axi
> + - const: jpeg
> +
> + interconnects:
> + maxItems: 3
> +
> + interconnect-names:
> + items:
> + - const: cpu-cfg
> + - const: hf-mnoc
> + - const: sf-mnoc
> +
> + iommus:
> + maxItems: 1
We've been asking people to name the IOMMU SID to ensure we are
including the right SID.
My own view is we should have a header for these items with a meaningful
name - separate argument I'm not asking for that here but please do add
a comment naming the SID.
> +
> + operating-points-v2: true
> +
> + opp-table:
> + type: object
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> + - clock-names
> + - interrupts
> + - interconnects
> + - interconnect-names
> + - iommus
If we have clocks and interconnects, there is no reason to omit
power-domains.
Otherwise looking pretty good to me, thanks for sticking with it.
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/qcom,camcc-sm8250.h>
> + #include <dt-bindings/clock/qcom,gcc-sm8250.h>
> + #include <dt-bindings/interconnect/qcom,icc.h>
> + #include <dt-bindings/interconnect/qcom,sm8250.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> + jpeg-encoder@ac53000 {
> + compatible = "qcom,sm8250-jenc";
> + reg = <0xac53000 0x1000>;
> +
> + interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
> +
> + clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
> + <&gcc GCC_CAMERA_SF_AXI_CLK>,
> + <&camcc CAM_CC_CORE_AHB_CLK>,
> + <&camcc CAM_CC_CPAS_AHB_CLK>,
> + <&camcc CAM_CC_CAMNOC_AXI_CLK>,
> + <&camcc CAM_CC_JPEG_CLK>;
> + clock-names = "hf_axi",
> + "sf_axi",
> + "core_ahb",
> + "cpas_ahb",
> + "cnoc_axi",
> + "jpeg";
> +
> + interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
> + &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
> + <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
> + <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
> + interconnect-names = "cpu-cfg",
> + "hf-mnoc",
> + "sf-mnoc";
> +
> + iommus = <&apps_smmu 0x2040 0x400>;
> +
> + operating-points-v2 = <&jpeg_opp_table>;
> +
> + jpeg_opp_table: opp-table {
> + compatible = "operating-points-v2";
> +
> + opp-100000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <100000000>;
> + opp-peak-kBps = <76800 104166 104166>;
> + opp-avg-kBps = <38400 33569 33569>;
> + required-opps = <&rpmhpd_opp_min_svs>;
> + };
> +
> + opp-200000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <200000000>;
> + opp-peak-kBps = <76800 208333 208333>;
> + opp-avg-kBps = <38400 67138 67138>;
> + required-opps = <&rpmhpd_opp_low_svs>;
> + };
> +
> + opp-400000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <400000000>;
> + opp-peak-kBps = <76800 416666 416666>;
> + opp-avg-kBps = <38400 134277 134277>;
> + required-opps = <&rpmhpd_opp_svs>;
> + };
> +
> + opp-480000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <480000000>;
> + opp-peak-kBps = <76800 500000 500000>;
> + opp-avg-kBps = <38400 161132 161132>;
> + required-opps = <&rpmhpd_opp_svs_l1>;
> + };
> +
> + opp-600000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <600000000>;
> + opp-peak-kBps = <76800 625000 625000>;
> + opp-avg-kBps = <38400 201416 201416>;
> + required-opps = <&rpmhpd_opp_nom>;
> + };
> + };
> + };
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
2026-07-06 7:46 ` Vladimir Zapolskiy
@ 2026-07-06 10:21 ` Bryan O'Donoghue
2026-07-06 12:19 ` Krzysztof Kozlowski
` (2 subsequent siblings)
4 siblings, 0 replies; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 10:21 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 08:11, Atanas Filipov wrote:
> +------------+-----------------------------+-------------------+
> | Resolution | HW v4l2jpegenc (q100) | SW jpegenc (q100) |
> | | Execution | avrFPS | maxFPS | Execution | FPS |
> +------------+-----------+--------+--------+-----------+-------+
> | 512x512 | 2.2332 | 1139.1 | 1195.4 | 7.1055 | 358.0 |
> | 1280x720 | 1.7300 | 418.5 | 457.2 | 5.0585 | 143.1 |
> | 1024x1024 | 1.6991 | 374.3 | 397.4 | 4.7304 | 134.4 |
> | 1920x1088 | 1.6596 | 192.8 | 193.8 | 3.7913 | 84.4 |
> | 2048x2048 | 1.7277 | 92.0 | 91.6 | 3.6343 | 43.7 |
> | 4096x4096 | 1.5887 | 25.2 | 25.3 | 4.2163 | 9.5 |
> | 8192x8192 | 1.3104 | 7.6 | 8.0 | 2.9987 | 3.3 |
> +------------+-----------+--------+--------+-----------+-------+
Nice thank you this will really help me verify/test your work.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 10:12 ` [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Bryan O'Donoghue
@ 2026-07-06 12:00 ` Vladimir Zapolskiy
2026-07-06 12:11 ` Bryan O'Donoghue
0 siblings, 1 reply; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 12:00 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 7/6/26 13:12, Bryan O'Donoghue wrote:
> On 06/07/2026 08:11, Atanas Filipov wrote:
>> Note: The handling of shared camera subsystem resources (power domains,
>> interconnects) for child IP blocks is still an open design question.
>
> Why ?
>
> A device needs to vote on its own interconnect and power-domains on any
> bus. A sub-device of another device may wish to ramp a clock for
> whatever reason.
Certainly a CAMSS device will vote on all needed to it resources, some of
which are shared and got their description under CAMSS device tree node.
> There is no "master" device in this block of devices - save perhaps for
> the CSID mux / wrappers on some of these parts.
>
> We have shared resources like camera noc, system noc and external clocks.
>
> Please include power-domains and interconnects.
>
Why? The common power domain and interconnects have already been
described as resources of the parent CAMSS device, there is no need
to duplicate descriptions in every child device tree node of CAMSS.
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
@ 2026-07-06 12:09 ` Krzysztof Kozlowski
2026-07-06 12:27 ` Bryan O'Donoghue
2026-07-06 12:41 ` Dmitry Baryshkov
1 sibling, 1 reply; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:09 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>
> Use devm_of_platform_populate() so that child nodes declared under the
> CAMSS device tree node (e.g. OPE) are automatically instantiated as
> platform devices. This is required now that CAMSS is modelled as a
> simple-bus and ISP blocks such as OPE are described as child nodes.
I do not understand this rationale. How is this required now? Are
existing DTS broken and do not work? Then this needs to be explained.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 12:00 ` Vladimir Zapolskiy
@ 2026-07-06 12:11 ` Bryan O'Donoghue
2026-07-06 13:02 ` Vladimir Zapolskiy
0 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 12:11 UTC (permalink / raw)
To: Vladimir Zapolskiy, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 06/07/2026 13:00, Vladimir Zapolskiy wrote:
>> There is no "master" device in this block of devices - save perhaps for
>> the CSID mux / wrappers on some of these parts.
>>
>> We have shared resources like camera noc, system noc and external clocks.
>>
>> Please include power-domains and interconnects.
>>
>
> Why? The common power domain and interconnects have already been
> described as resources of the parent CAMSS device, there is no need
> to duplicate descriptions in every child device tree node of CAMSS.
CSID, IFE etc are not parents of the JPEG encoder.
The idea is to make all of these peers, eventually. Not particular block
owns the TITAN_TOP_GDSC - the entire CAMSS block is a collapsible block.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node
2026-07-06 7:11 ` [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node Atanas Filipov
2026-07-06 10:07 ` Bryan O'Donoghue
@ 2026-07-06 12:11 ` Krzysztof Kozlowski
1 sibling, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:11 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> The CAMSS node uses #address-cells = <2>, #size-cells = <2> and ranges
> to act as a bus for child IP blocks such as the JPEG encoder. Add these
> properties to the binding so that dtbs_check does not reject them.
There are no children, so above are not valid.
>
> Use unevaluatedProperties: false instead of additionalProperties: false
> so that child nodes are validated against their own schema (matched by
This is not the purpose of unevaluatedProperties and it is not even true.
> compatible) without requiring the parent binding to enumerate each child
> type explicitly. This is the correct approach for a bus-like container
> node and avoids updating the camss binding for every new child device
> added in the future.
>
> No functional change.
I see significant functional changes - you switched to unevaluatedProperties
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../devicetree/bindings/media/qcom,sm8250-camss.yaml | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> index a509d4bbcb4a..6008938ee369 100644
> --- a/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,sm8250-camss.yaml
> @@ -102,6 +102,14 @@ properties:
> - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
> - description: Titan GDSC - Titan ISP Block, Global Distributed Switch Controller.
>
> + "#address-cells":
> + const: 2
> +
> + "#size-cells":
> + const: 2
> +
> + ranges: true
> +
> ports:
> $ref: /schemas/graph.yaml#/properties/ports
>
> @@ -317,7 +325,7 @@ required:
> - vdda-phy-supply
> - vdda-pll-supply
>
> -additionalProperties: false
> +unevaluatedProperties: false
No, NAK. I do not see how this patch makes any sense.
>
> examples:
> - |
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding
2026-07-06 7:11 ` [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding Atanas Filipov
2026-07-06 10:19 ` Bryan O'Donoghue
@ 2026-07-06 12:17 ` Krzysztof Kozlowski
1 sibling, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:17 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> Add device-tree binding for the Qualcomm JPEG encoder hardware block
> present in SM8250 (Kona) SoCs.
>
You still ignored my comments from v1. You never responded to them,
never implemented. This is fifth version and you still ignore them.
Please go to the v1 and respond to EACH COMMENT that you acknowledge it
and going to implement it.
Otherwise I do not see how can we accept this, if you keep ignoring
maintainer's feedback, even if that feedback is about trivial things.
NAK
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node
2026-07-06 7:11 ` [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node Atanas Filipov
@ 2026-07-06 12:18 ` Krzysztof Kozlowski
2026-07-06 12:45 ` Dmitry Baryshkov
1 sibling, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:18 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> Add the JPEG encoder hardware node to the SM8250 device tree so the
> qcom-jpeg V4L2 encoder driver can bind and operate on this platform.
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/sm8250.dtsi | 81 ++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
Why is DTS patch before driver? Driver CANNOT depend on this.
Please organize your work correctly, see submitting patches in DT
explaining how this should be done.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
2026-07-06 7:46 ` Vladimir Zapolskiy
2026-07-06 10:21 ` Bryan O'Donoghue
@ 2026-07-06 12:19 ` Krzysztof Kozlowski
2026-07-06 13:11 ` Dmitry Baryshkov
2026-07-06 17:13 ` Uwe Kleine-König
4 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:19 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> Add a Qualcomm JPEG encoder driver implemented on top of the
> V4L2 mem2mem framework.
>
> The driver wires vb2 queue handling, format negotiation, JPEG header
> handling, interrupt-driven job completion, and runtime PM/clock/ICC
> integration for the standalone JPEG encode hardware block.
>
> This series targets SM8250 (Kona) platforms.
>
> The jpeg-encoder node is described as a child node of the CAMSS block
> and is probed automatically via of_platform_populate() in camss_probe().
>
> Usage examples:
>
> - Check of related video node: v4l2-ctl --list-devices
> The expected result:
> qcom-jpeg-enc (platform:qcom-jpeg-enc):
> /dev/videoX
>
> V4L2 Examples:
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap
>
> v4l2-ctl -d /dev/video14 \
> -c compression_quality=100 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --stream-count=100
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=1920,height=1080,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --set-ctrl=perf_level_auto=1 \
> --set-ctrl=fps_target=30 \
> --stream-count=100
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=8192,height=8192,pixelformat=NM12 \
> --stream-mmap --stream-out-mmap \
> --stream-count=1 \
> --stream-to=8192x8192_NM12.jpg
>
> v4l2-ctl -d /dev/video14 \
> --set-fmt-video-out=width=8192,height=8192,pixelformat=GREY \
> --stream-mmap --stream-out-mmap \
> --stream-count=1 \
> --stream-to=OUT_8192x8192.jpg
>
> v4l2-ctl -d /dev/video14 \
> -c compression_quality=100 \
> --set-fmt-video-out=width=1920,height=1088,pixelformat=NM12 \
> --stream-mmap=4 --stream-out-mmap=4 \
> --stream-count=100 \
> --stream-out-pattern=21 \
> --stream-to=OUT_1920x1088_100F.mjpg
>
> GStreamer examples:
>
> gst-launch-1.0 videotestsrc \
> is-live=false pattern=ball num-buffers=50 ! \
> video/x-raw,format=NV12,width=1920,height=1080 ! \
> videoscale ! videoconvert ! \
> video/x-raw,format=NV12,width=1920,height=1088 ! \
> v4l2jpegenc ! \
> fakesink sync=false
>
> gst-launch-1.0 videotestsrc is-live=true pattern=smpte ! \
> video/x-raw,format=NV12,width=1920,height=1088 ! \
> v4l2jpegenc extra-controls="controls,compression_quality=85" ! \
> fpsdisplaysink -v sync=false
>
> Performance measurements on SM8250
> (NV12, quality=100, GStreamer v4l2jpegenc vs jpegenc):
>
> +------------+-----------------------------+-------------------+
> | Resolution | HW v4l2jpegenc (q100) | SW jpegenc (q100) |
> | | Execution | avrFPS | maxFPS | Execution | FPS |
> +------------+-----------+--------+--------+-----------+-------+
> | 512x512 | 2.2332 | 1139.1 | 1195.4 | 7.1055 | 358.0 |
> | 1280x720 | 1.7300 | 418.5 | 457.2 | 5.0585 | 143.1 |
> | 1024x1024 | 1.6991 | 374.3 | 397.4 | 4.7304 | 134.4 |
> | 1920x1088 | 1.6596 | 192.8 | 193.8 | 3.7913 | 84.4 |
> | 2048x2048 | 1.7277 | 92.0 | 91.6 | 3.6343 | 43.7 |
> | 4096x4096 | 1.5887 | 25.2 | 25.3 | 4.2163 | 9.5 |
> | 8192x8192 | 1.3104 | 7.6 | 8.0 | 2.9987 | 3.3 |
> +------------+-----------+--------+--------+-----------+-------+
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> .../bindings/media/qcom,jpeg-encoder.yaml | 143 +-
NAK
...
> diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig
> index 4f4d3a68e6e5..f33d53a754a0 100644
> --- a/drivers/media/platform/qcom/Kconfig
> +++ b/drivers/media/platform/qcom/Kconfig
> @@ -5,3 +5,4 @@ comment "Qualcomm media platform drivers"
> source "drivers/media/platform/qcom/camss/Kconfig"
> source "drivers/media/platform/qcom/iris/Kconfig"
> source "drivers/media/platform/qcom/venus/Kconfig"
> +source "drivers/media/platform/qcom/jpeg/Kconfig"
Just like for other patch, you ignored reviewers' feedback.
I already asked you to go through previous comments around v3. I do not
see improvements, so I assume you do it deliberately.
NAK
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
` (5 preceding siblings ...)
2026-07-06 10:12 ` [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Bryan O'Donoghue
@ 2026-07-06 12:24 ` Krzysztof Kozlowski
6 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:24 UTC (permalink / raw)
To: Atanas Filipov, linux-media
Cc: bryan.odonoghue, vladimir.zapolskiy, loic.poulain, mchehab, robh,
krzk+dt, conor+dt, andersson, konradybcio, linux-arm-msm,
linux-kernel, devicetree
On 06/07/2026 09:11, Atanas Filipov wrote:
> This series adds support for the Qualcomm JPEG V4L2 mem2mem encoder on SM8250.
>
> The goal is to upstream a standards-based JPEG encode path using the V4L2 M2M
> framework, with DT-described hardware resources and SoC DTS integration.
>
1. Here was NAK for v2:
https://lore.kernel.org/all/20260625133828.3221781-1-atanas.filipov@oss.qualcomm.com/
When I asked to implement previous comments.
Did you implement them? No.
2. At v2-1 version I already asked you to implement comments:
https://lore.kernel.org/all/c9006b67-aa43-41d3-8b13-f20dd2e544c6@kernel.org/
Basically now, THIRD TIME, you send the same and you still do not
implement the comments I asked you at v1.
I treat it as deliberate action to ignore review. Me asking the same for
the third time and expecting different results goes towards insanity, so
let's avoid me becoming insane and consider all future submissions also
NAK-ed:
Nacked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 12:09 ` Krzysztof Kozlowski
@ 2026-07-06 12:27 ` Bryan O'Donoghue
2026-07-06 12:36 ` Krzysztof Kozlowski
0 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 12:27 UTC (permalink / raw)
To: Krzysztof Kozlowski, Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 13:09, Krzysztof Kozlowski wrote:
> On 06/07/2026 09:11, Atanas Filipov wrote:
>> From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>>
>> Use devm_of_platform_populate() so that child nodes declared under the
>> CAMSS device tree node (e.g. OPE) are automatically instantiated as
>> platform devices. This is required now that CAMSS is modelled as a
>> simple-bus and ISP blocks such as OPE are described as child nodes.
>
> I do not understand this rationale. How is this required now? Are
> existing DTS broken and do not work? Then this needs to be explained.
Required is wrong, true. Existing DT is not broken no.
Should read something like:
"Use devm_of_platform_populate() so that child nodes declared under the
CAMSS device tree node (e.g. OPE) are automatically instantiated as
platform devices."
Why this patch ? We also discussed simple-mfd but then settled on this
way instead.
https://lore.kernel.org/all/0f898ad3-156d-496c-b2c1-88646edc7ab6@kernel.org/
> Best regards,
> Krzysztof
Not withstanding this series, I'll fixup my version of this patch ;)
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 12:27 ` Bryan O'Donoghue
@ 2026-07-06 12:36 ` Krzysztof Kozlowski
2026-07-06 12:38 ` Bryan O'Donoghue
0 siblings, 1 reply; 33+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-06 12:36 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 14:27, Bryan O'Donoghue wrote:
> On 06/07/2026 13:09, Krzysztof Kozlowski wrote:
>> On 06/07/2026 09:11, Atanas Filipov wrote:
>>> From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>>>
>>> Use devm_of_platform_populate() so that child nodes declared under the
>>> CAMSS device tree node (e.g. OPE) are automatically instantiated as
>>> platform devices. This is required now that CAMSS is modelled as a
>>> simple-bus and ISP blocks such as OPE are described as child nodes.
>>
>> I do not understand this rationale. How is this required now? Are
>> existing DTS broken and do not work? Then this needs to be explained.
>
> Required is wrong, true. Existing DT is not broken no.
>
> Should read something like:
>
> "Use devm_of_platform_populate() so that child nodes declared under the
> CAMSS device tree node (e.g. OPE) are automatically instantiated as
> platform devices."
This patch then makes no sense before the binding defines such children.
If you carry this patch outside of this work, you do not need to keep
the Nacked-by tag. Only constant ignoring of review here is the reason
of Nacked-by.
However if you carry this patch separately, you still need to fix the
lack of sense of doing this at this point.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 12:36 ` Krzysztof Kozlowski
@ 2026-07-06 12:38 ` Bryan O'Donoghue
0 siblings, 0 replies; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 12:38 UTC (permalink / raw)
To: Krzysztof Kozlowski, Atanas Filipov, linux-media
Cc: vladimir.zapolskiy, loic.poulain, mchehab, robh, krzk+dt,
conor+dt, andersson, konradybcio, linux-arm-msm, linux-kernel,
devicetree
On 06/07/2026 13:36, Krzysztof Kozlowski wrote:
> On 06/07/2026 14:27, Bryan O'Donoghue wrote:
>> On 06/07/2026 13:09, Krzysztof Kozlowski wrote:
>>> On 06/07/2026 09:11, Atanas Filipov wrote:
>>>> From: Bryan O'Donoghue<bryan.odonoghue@linaro.org>
>>>>
>>>> Use devm_of_platform_populate() so that child nodes declared under the
>>>> CAMSS device tree node (e.g. OPE) are automatically instantiated as
>>>> platform devices. This is required now that CAMSS is modelled as a
>>>> simple-bus and ISP blocks such as OPE are described as child nodes.
>>> I do not understand this rationale. How is this required now? Are
>>> existing DTS broken and do not work? Then this needs to be explained.
>> Required is wrong, true. Existing DT is not broken no.
>>
>> Should read something like:
>>
>> "Use devm_of_platform_populate() so that child nodes declared under the
>> CAMSS device tree node (e.g. OPE) are automatically instantiated as
>> platform devices."
> This patch then makes no sense before the binding defines such children.
>
> If you carry this patch outside of this work, you do not need to keep
> the Nacked-by tag. Only constant ignoring of review here is the reason
> of Nacked-by.
>
> However if you carry this patch separately, you still need to fix the
> lack of sense of doing this at this point.
>
> Best regards,
> Krzysztof
No I agree. It only makes sense to propose this patch after adding a DT
that cares about it.
The ordering here doesn't make sense.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
2026-07-06 12:09 ` Krzysztof Kozlowski
@ 2026-07-06 12:41 ` Dmitry Baryshkov
1 sibling, 0 replies; 33+ messages in thread
From: Dmitry Baryshkov @ 2026-07-06 12:41 UTC (permalink / raw)
To: Atanas Filipov
Cc: linux-media, bryan.odonoghue, vladimir.zapolskiy, loic.poulain,
mchehab, robh, krzk+dt, conor+dt, andersson, konradybcio,
linux-arm-msm, linux-kernel, devicetree
On Mon, Jul 06, 2026 at 10:11:09AM +0300, Atanas Filipov wrote:
> From: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>
> Use devm_of_platform_populate() so that child nodes declared under the
> CAMSS device tree node (e.g. OPE) are automatically instantiated as
> platform devices. This is required now that CAMSS is modelled as a
> simple-bus and ISP blocks such as OPE are described as child nodes.
>
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
> Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> drivers/media/platform/qcom/camss/camss.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
> index 2123f6388e3d..95e655a8b6aa 100644
> --- a/drivers/media/platform/qcom/camss/camss.c
> +++ b/drivers/media/platform/qcom/camss/camss.c
> @@ -16,6 +16,7 @@
> #include <linux/of.h>
> #include <linux/of_device.h>
> #include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> #include <linux/pm_runtime.h>
> #include <linux/pm_domain.h>
> #include <linux/slab.h>
> @@ -5362,6 +5363,8 @@ static int camss_probe(struct platform_device *pdev)
> if (!camss)
> return -ENOMEM;
>
> + devm_of_platform_populate(dev);
Don't ignore the error code. Also, are the children ready to be probed
at this point? Usually, it should happen _After_ the camss is being
fully set up.
> +
> camss->res = of_device_get_match_data(dev);
>
> atomic_set(&camss->ref_count, 0);
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node
2026-07-06 7:11 ` [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node Atanas Filipov
2026-07-06 12:18 ` Krzysztof Kozlowski
@ 2026-07-06 12:45 ` Dmitry Baryshkov
1 sibling, 0 replies; 33+ messages in thread
From: Dmitry Baryshkov @ 2026-07-06 12:45 UTC (permalink / raw)
To: Atanas Filipov
Cc: linux-media, bryan.odonoghue, vladimir.zapolskiy, loic.poulain,
mchehab, robh, krzk+dt, conor+dt, andersson, konradybcio,
linux-arm-msm, linux-kernel, devicetree
On Mon, Jul 06, 2026 at 10:11:12AM +0300, Atanas Filipov wrote:
> Add the JPEG encoder hardware node to the SM8250 device tree so the
> qcom-jpeg V4L2 encoder driver can bind and operate on this platform.
>
> Signed-off-by: Atanas Filipov <atanas.filipov@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/sm8250.dtsi | 81 ++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> index 7076720413ab..a089b706f60c 100644
> --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> @@ -12,6 +12,7 @@
> #include <dt-bindings/dma/qcom-gpi.h>
> #include <dt-bindings/gpio/gpio.h>
> #include <dt-bindings/interconnect/qcom,osm-l3.h>
> +#include <dt-bindings/interconnect/qcom,icc.h>
> #include <dt-bindings/interconnect/qcom,sm8250.h>
> #include <dt-bindings/mailbox/qcom-ipcc.h>
> #include <dt-bindings/phy/phy-qcom-qmp.h>
> @@ -4471,6 +4472,10 @@ cci1_i2c1: i2c-bus@1 {
>
> camss: camss@ac6a000 {
> compatible = "qcom,sm8250-camss";
> + #address-cells = <2>;
> + #size-cells = <2>;
> + ranges;
> +
> status = "disabled";
>
> reg = <0 0x0ac6a000 0 0x2000>,
> @@ -4649,6 +4654,82 @@ port@5 {
> reg = <5>;
> };
> };
> +
> + jpeg-encoder@ac53000 {
> + compatible = "qcom,sm8250-jenc";
> +
> + reg = <0 0x0ac53000 0 0x1000>;
0x0 instead of just 0
> +
> + interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
> +
> + clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
> + <&gcc GCC_CAMERA_SF_AXI_CLK>,
> + <&camcc CAM_CC_CORE_AHB_CLK>,
> + <&camcc CAM_CC_CPAS_AHB_CLK>,
> + <&camcc CAM_CC_CAMNOC_AXI_CLK>,
> + <&camcc CAM_CC_JPEG_CLK>;
> +
> + clock-names = "hf_axi",
> + "sf_axi",
> + "core_ahb",
> + "cpas_ahb",
> + "cnoc_axi",
> + "jpeg";
What are the clock _inputs_ on the JPEG core side?
> +
> + interconnects = <&gem_noc MASTER_AMPSS_M0
> + QCOM_ICC_TAG_ACTIVE_ONLY
Put tags on the previous line (even if it is longer than 80 chars).
> + &config_noc SLAVE_CAMERA_CFG
> + QCOM_ICC_TAG_ACTIVE_ONLY>,
> + <&mmss_noc MASTER_CAMNOC_HF
> + QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0
> + QCOM_ICC_TAG_ALWAYS>,
> + <&mmss_noc MASTER_CAMNOC_SF
> + QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI_CH0
> + QCOM_ICC_TAG_ALWAYS>;
> + interconnect-names = "cpu-cfg",
> + "hf-mnoc",
> + "sf-mnoc";
> +
> + iommus = <&apps_smmu 0x2040 0x400>;
> +
> + operating-points-v2 = <&jpeg_opp_table>;
> +
> + jpeg_opp_table: opp-table {
> + compatible = "operating-points-v2";
> +
> + opp-100000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <100000000>;
> + required-opps = <&rpmhpd_opp_min_svs>;
Inside the DT example i saw bandwidth votes. Why are they gone from the
DT?
> + };
> +
> + opp-200000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <200000000>;
> + required-opps = <&rpmhpd_opp_low_svs>;
> + };
> +
> + opp-400000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <400000000>;
> + required-opps = <&rpmhpd_opp_svs>;
> + };
> +
> + opp-480000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <480000000>;
> + required-opps = <&rpmhpd_opp_svs_l1>;
> + };
> +
> + opp-600000000 {
> + opp-hz = /bits/ 64 <400000000>,
> + /bits/ 64 <600000000>;
> + required-opps = <&rpmhpd_opp_nom>;
> + };
> + };
> + };
> };
>
> camcc: clock-controller@ad00000 {
> --
> 2.34.1
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 12:11 ` Bryan O'Donoghue
@ 2026-07-06 13:02 ` Vladimir Zapolskiy
2026-07-06 13:37 ` Bryan O'Donoghue
0 siblings, 1 reply; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 13:02 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 7/6/26 15:11, Bryan O'Donoghue wrote:
> On 06/07/2026 13:00, Vladimir Zapolskiy wrote:
>>> There is no "master" device in this block of devices - save perhaps for
>>> the CSID mux / wrappers on some of these parts.
>>>
>>> We have shared resources like camera noc, system noc and external clocks.
>>>
>>> Please include power-domains and interconnects.
>>>
>>
>> Why? The common power domain and interconnects have already been
>> described as resources of the parent CAMSS device, there is no need
>> to duplicate descriptions in every child device tree node of CAMSS.
>
> CSID, IFE etc are not parents of the JPEG encoder.
>
> The idea is to make all of these peers, eventually. Not particular block
> owns the TITAN_TOP_GDSC - the entire CAMSS block is a collapsible block.
>
The statement provides the same information as before, e.g. it's found here:
https://lore.kernel.org/linux-media/fbc018f5-c025-4747-85f2-53b45b0f0496@nxsw.ie/
There's no functional linkage between CAMSS/IFE and JPEG - they are
peers within the CAMSS power-island.
Thus "the CAMSS power-island" exists, and it got its hardware description
as the CAMSS top device tree node, and TITAN_TOP_GDSC power domain is
a natural resource of all devices on "CAMSS power-island", including the
ones got their description inline CAMSS device tree node or children nodes.
There is a difference between 1 point of resource description and N points
of resource descriptions, the latter one is more complex and error prone,
therefore I belive it shall be disregarded in favour of the former option.
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
` (2 preceding siblings ...)
2026-07-06 12:19 ` Krzysztof Kozlowski
@ 2026-07-06 13:11 ` Dmitry Baryshkov
2026-07-06 17:13 ` Uwe Kleine-König
4 siblings, 0 replies; 33+ messages in thread
From: Dmitry Baryshkov @ 2026-07-06 13:11 UTC (permalink / raw)
To: Atanas Filipov
Cc: linux-media, bryan.odonoghue, vladimir.zapolskiy, loic.poulain,
mchehab, robh, krzk+dt, conor+dt, andersson, konradybcio,
linux-arm-msm, linux-kernel, devicetree
On Mon, Jul 06, 2026 at 10:11:13AM +0300, Atanas Filipov wrote:
> Add a Qualcomm JPEG encoder driver implemented on top of the
> V4L2 mem2mem framework.
>
> The driver wires vb2 queue handling, format negotiation, JPEG header
> handling, interrupt-driven job completion, and runtime PM/clock/ICC
> integration for the standalone JPEG encode hardware block.
>
> This series targets SM8250 (Kona) platforms.
>
> The jpeg-encoder node is described as a child node of the CAMSS block
> and is probed automatically via of_platform_populate() in camss_probe().
>
> Usage examples:
>
> - Check of related video node: v4l2-ctl --list-devices
> The expected result:
> qcom-jpeg-enc (platform:qcom-jpeg-enc):
> /dev/videoX
>
>
> diff --git a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> index e4c16388ef07..53e83ebe4699 100644
> --- a/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> +++ b/Documentation/devicetree/bindings/media/qcom,jpeg-encoder.yaml
> @@ -72,80 +72,75 @@ examples:
> #include <dt-bindings/interconnect/qcom,sm8250.h>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
>
> - jpeg-encoder@ac53000 {
> - compatible = "qcom,sm8250-jenc";
> - reg = <0xac53000 0x1000>;
What is going on here? And why?
> -
> - interrupts = <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>;
> -
> - clocks = <&gcc GCC_CAMERA_HF_AXI_CLK>,
> - <&gcc GCC_CAMERA_SF_AXI_CLK>,
> - <&camcc CAM_CC_CORE_AHB_CLK>,
> - <&camcc CAM_CC_CPAS_AHB_CLK>,
> - <&camcc CAM_CC_CAMNOC_AXI_CLK>,
> - <&camcc CAM_CC_JPEG_CLK>;
> - clock-names = "hf_axi",
> - "sf_axi",
> - "core_ahb",
> - "cpas_ahb",
> - "cnoc_axi",
> - "jpeg";
> -
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h b/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
> new file mode 100644
> index 000000000000..2ab29bfb9b88
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_defs.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef QCOM_JENC_DEFS_H
> +#define QCOM_JENC_DEFS_H
> +
> +#include <linux/types.h>
> +#include <uapi/linux/v4l2-controls.h>
There includes are not necessary for this header.
> +
> +/* Offline JPEG encoder constraints */
> +#define QCOM_JPEG_HW_MAX_WIDTH 8192
> +#define QCOM_JPEG_HW_MAX_HEIGHT 8192
> +#define QCOM_JPEG_HW_MIN_WIDTH 256
> +#define QCOM_JPEG_HW_MIN_HEIGHT 256
> +
> +#define QCOM_JPEG_HW_DEF_HSTEP 16
> +#define QCOM_JPEG_HW_DEF_VSTEP 16
> +
> +#define QCOM_JPEG_HW_DEF_WIDTH 1920
> +#define QCOM_JPEG_HW_DEF_HEIGHT 1088
> +
> +#define QCOM_JPEG_MAX_PLANES 3
> +
> +#define QCOM_JPEG_QUALITY_MIN 1
> +#define QCOM_JPEG_QUALITY_DEF 98
> +#define QCOM_JPEG_QUALITY_MAX 100
> +#define QCOM_JPEG_QUALITY_MID (QCOM_JPEG_QUALITY_MAX / 2)
> +#define QCOM_JPEG_QUALITY_UNT 1
> +
> +#define QCOM_JPEG_FPS_MIN 1
> +#define QCOM_JPEG_FPS_MAX 240
> +#define QCOM_JPEG_FPS_DEF 30
> +#define QCOM_JPEG_FPS_UNT 1
This is a collection of random defines, which are mostly used once.
Please move them to the corresponding source file.
> +
> +#endif /* QCOM_JENC_DEFS_H */
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
> new file mode 100644
> index 000000000000..ddfa84838b6b
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_dev.c
> @@ -0,0 +1,314 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "qcom_jenc_dev.h"
> +#include "qcom_jenc_ops.h"
> +#include "qcom_jenc_res.h"
> +#include "qcom_jenc_v4l2.h"
> +
> +enum jpeg_opp_clks_id {
> + JPEG_OPP_CNOC_IDX = 0,
> + JPEG_OPP_CORE_IDX
> +};
> +
> +static const char * const opp_clk_names[] = {
> + [JPEG_OPP_CNOC_IDX] = "cnoc_axi",
This way your driver will force its own frequency on the CNOC_AXI clock.
There are other clients of the clock, so there should be some kind of
voting on it. Either use icc-clk or add CAMSS API to sum the votes on
the CNOC_AXI.
> + [JPEG_OPP_CORE_IDX] = "jpeg",
> + NULL,
> +};
> +
> +static struct dev_pm_opp_config opp_config = {
> + .clk_names = opp_clk_names,
> + .config_clks = dev_pm_opp_config_clks_simple,
> +};
> +
> +static int qcom_jpeg_opp_init(struct qcom_jenc_dev *jenc)
> +{
> + struct dev_pm_opp *opp;
> + int rc;
> +
> + rc = devm_pm_opp_set_config(jenc->dev, &opp_config);
> + if (rc)
> + return rc;
> +
> + rc = devm_pm_opp_of_add_table(jenc->dev);
> + if (rc && rc != -ENODEV)
> + return rc;
> +
> + /* initialize the maximum available frequency for the JPEG core */
> + jenc->max_freq = ULONG_MAX;
> + opp = dev_pm_opp_find_freq_floor_indexed(jenc->dev, &jenc->max_freq, JPEG_OPP_CORE_IDX);
> + if (IS_ERR(opp))
> + return PTR_ERR(opp);
> +
> + dev_pm_opp_put(opp);
> +
> + /* initialize the default optimized frequency for the JPEG core */
> + jenc->opt_freq = jenc->max_freq;
> +
> + dev_dbg(jenc->dev, "JPEG max clocks is: %lu\n", jenc->max_freq);
> +
> + return 0;
> +}
> +
> +static int qcom_jpeg_clk_init(struct qcom_jenc_dev *jenc)
> +{
> + jenc->num_clks = devm_clk_bulk_get_all(jenc->dev, &jenc->clks);
> + if (jenc->num_clks < 0)
> + return jenc->num_clks;
> +
> + return 0;
> +}
> +
> +static int qcom_jpeg_clk_on(struct qcom_jenc_dev *jenc)
> +{
> + struct dev_pm_opp *opp;
> + int rc;
> +
> + rc = clk_bulk_prepare_enable(jenc->num_clks, jenc->clks);
> + if (rc)
> + return rc;
> +
> + /* setup the OPP according to the calculated optimal frequency */
> + opp = dev_pm_opp_find_freq_ceil_indexed(jenc->dev, &jenc->opt_freq, JPEG_OPP_CORE_IDX);
> + if (IS_ERR(opp)) {
> + rc = PTR_ERR(opp);
> + goto err_clk_disable;
> + }
> +
> + rc = dev_pm_opp_set_opp(jenc->dev, opp);
> + if (rc)
> + goto err_dev_pm_opp;
> +
> + dev_dbg(jenc->dev, "selected OPP clocks cnoc=%lu, core=%lu\n",
> + dev_pm_opp_get_freq_indexed(opp, JPEG_OPP_CNOC_IDX),
> + dev_pm_opp_get_freq_indexed(opp, JPEG_OPP_CORE_IDX));
Drop extra debugging, you can enable debugging for OPP via
CONFIG_DEBUG_DRIVER.
> +
> + dev_pm_opp_put(opp);
> +
> + return 0;
> +
> +err_dev_pm_opp:
> + dev_pm_opp_put(opp);
Once you drop excessive debugging code, the dev_pm_opp_put() will find
its natural place right after dev_pm_opp_set_opp(), before checking the
rc.
> +err_clk_disable:
> + clk_bulk_disable_unprepare(jenc->num_clks, jenc->clks);
> +
> + return rc;
> +}
> +
> +static void qcom_jpeg_clk_off(struct qcom_jenc_dev *jenc)
> +{
> + dev_pm_opp_set_opp(jenc->dev, NULL);
> + clk_bulk_disable_unprepare(jenc->num_clks, jenc->clks);
> + jenc->opt_freq = jenc->max_freq;
> +}
> +
> +/* qcom_jpeg_camss_get - resume the parent CAMSS device */
> +static int qcom_jpeg_camss_get(struct qcom_jenc_dev *jenc)
> +{
> + return pm_runtime_resume_and_get(jenc->camss_dev);
Use devlinks instead.
> +}
> +
> +/* qcom_jpeg_camss_put - release the parent CAMSS device */
> +static void qcom_jpeg_camss_put(struct qcom_jenc_dev *jenc)
> +{
> + pm_runtime_put_sync(jenc->camss_dev);
> +}
> +
> +static int qcom_jpeg_pm_suspend(struct device *dev)
> +{
> + struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
> +
> + qcom_jpeg_clk_off(jenc);
> + qcom_jpeg_camss_put(jenc);
> +
> + return 0;
> +}
> +
> +static int qcom_jpeg_pm_resume(struct device *dev)
> +{
> + struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
> + int rc;
> +
> + rc = qcom_jpeg_camss_get(jenc);
> + if (rc)
> + return rc;
> +
> + rc = qcom_jpeg_clk_on(jenc);
> + if (rc) {
> + qcom_jpeg_camss_put(jenc);
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_jpeg_pm_system_suspend(struct device *dev)
> +{
> + struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
> + int rc;
> +
> + v4l2_m2m_suspend(jenc->m2m_dev);
> +
> + rc = pm_runtime_force_suspend(dev);
> + if (rc)
> + v4l2_m2m_resume(jenc->m2m_dev);
> +
> + return rc;
> +}
> +
> +static int qcom_jpeg_pm_system_resume(struct device *dev)
> +{
> + struct qcom_jenc_dev *jenc = dev_get_drvdata(dev);
> + int rc;
> +
> + rc = pm_runtime_force_resume(dev);
> + if (rc)
> + return rc;
> +
> + v4l2_m2m_resume(jenc->m2m_dev);
> +
> + return 0;
> +}
> +
> +static _DEFINE_DEV_PM_OPS(qcom_jpeg_pm_ops,
> + qcom_jpeg_pm_system_suspend, qcom_jpeg_pm_system_resume,
> + qcom_jpeg_pm_suspend, qcom_jpeg_pm_resume, NULL);
> +
> +static int qcom_jpeg_probe(struct platform_device *pdev)
> +{
> + const struct qcom_dev_resources *res;
> + struct qcom_jenc_dev *jenc;
> + int rc;
> +
> + jenc = devm_kzalloc(&pdev->dev, sizeof(*jenc), GFP_KERNEL);
> + if (!jenc)
> + return -ENOMEM;
> +
> + jenc->dev = &pdev->dev;
> + jenc->camss_dev = pdev->dev.parent;
> + platform_set_drvdata(pdev, jenc);
> + rc = devm_mutex_init(&pdev->dev, &jenc->dev_mutex);
> + if (rc)
> + goto err_free_jenc;
> + spin_lock_init(&jenc->hw_lock);
> + init_completion(&jenc->reset_complete);
> + init_completion(&jenc->stop_complete);
> +
> + res = device_get_match_data(jenc->dev);
> + if (!res) {
> + rc = dev_err_probe(jenc->dev, -ENODEV, "unsupported SoC\n");
> + goto err_free_jenc;
drop the gotos, it's an empty label now.
> + }
> + jenc->res = res;
> +
> + if (!jenc->res->hw_ops) {
> + rc = dev_err_probe(jenc->dev, -EINVAL, "missing hw resources\n");
> + goto err_free_jenc;
> + }
> +
> + rc = dma_set_mask_and_coherent(jenc->dev, DMA_BIT_MASK(32));
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to set DMA mask\n");
> + goto err_free_jenc;
> + }
> +
> + jenc->jpeg_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(jenc->jpeg_base)) {
> + rc = dev_err_probe(jenc->dev, PTR_ERR(jenc->jpeg_base),
> + "failed to map JPEG resource\n");
> + goto err_free_jenc;
> + }
> +
> + rc = qcom_jpeg_opp_init(jenc);
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to init OPP\n");
> + goto err_free_jenc;
> + }
> +
> + rc = qcom_jpeg_clk_init(jenc);
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to init clocks\n");
> + goto err_free_jenc;
> + }
> +
> + jenc->irq = platform_get_irq(pdev, 0);
> + if (jenc->irq < 0) {
> + rc = dev_err_probe(jenc->dev, jenc->irq, "failed to get IRQ\n");
> + goto err_free_jenc;
> + }
> +
> + rc = devm_request_threaded_irq(jenc->dev, jenc->irq,
> + jenc->res->hw_ops->hw_irq_top,
> + jenc->res->hw_ops->hw_irq_bot,
> + IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(jenc->dev), jenc);
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to request IRQ\n");
> + goto err_free_jenc;
> + }
> +
> + rc = v4l2_device_register(jenc->dev, &jenc->v4l2_dev);
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to register V4L2 device\n");
> + goto err_free_jenc;
> + }
> +
> + rc = devm_add_action_or_reset(jenc->dev,
> + (void (*)(void *))v4l2_device_unregister,
> + &jenc->v4l2_dev);
> + if (rc)
> + goto err_free_jenc;
> +
> + rc = devm_pm_runtime_enable(jenc->dev);
> + if (rc)
> + goto err_free_jenc;
> +
> + rc = qcom_jpeg_v4l2_register(jenc);
> + if (rc) {
> + dev_err_probe(jenc->dev, rc, "failed to register video device\n");
> + goto err_free_jenc;
> + }
> +
> + dev_dbg(jenc->dev, "Qualcomm JPEG encoder registered\n");
> +
> + return 0;
> +
> +err_free_jenc:
> + return rc;
> +}
> +
[...]
> diff --git a/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
> new file mode 100644
> index 000000000000..c9959518c64d
> --- /dev/null
> +++ b/drivers/media/platform/qcom/jpeg/qcom_jenc_hdr.c
> @@ -0,0 +1,331 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +
> +#include <media/jpeg.h>
> +#include <media/v4l2-jpeg.h>
> +
> +#include "qcom_jenc_dev.h"
> +#include "qcom_jenc_hdr.h"
> +
> +/*
> + * The elements defined in this header are specified
> + * in the ITU-T T.81 / JPEG specification.
> + *
> + * https://www.w3.org/Graphics/JPEG/itu-t81.pdf
> + */
> +
> +#define JFIF_HEADER_WIDTH_OFFS 0x07
> +#define JFIF_HEADER_HEIGHT_OFFS 0x05
This is offset_of(), no need to define those.
I think, you already got a review from me. Move all standard-related
defines and code to the generic v4l2 code, unless you get an explicit
blessing from one of V4L2 maintainers not to do so.
> +#define JFIF_APP0_LENGTH_HI 0x00
> +#define JFIF_APP0_LENGTH_LO 0x10
> +#define JFIF_IDENT_TERM 0x00
> +#define JFIF_VERSION_MAJOR 0x01
> +#define JFIF_VERSION_MINOR 0x01
> +#define JFIF_DENSITY_HI 0x00
> +#define JFIF_DENSITY_LO 0x01
> +#define JFIF_THUMBNAIL_SIZE 0x00
> +
> +#define JPEG_SEG_LEN_HI 0x00
> +#define JPEG_LEN_DQT_LUMA_LO 0x43
> +#define JPEG_LEN_DQT_CHROMA_LO 0x43
> +#define JPEG_LEN_SOF0_MONO_LO 0x0b
> +#define JPEG_LEN_SOF0_COLOR_LO 0x11
> +#define JPEG_LEN_DHT_MONO_LO 0xd2
> +#define JPEG_LEN_DHT_COLOR_HI 0x01
> +#define JPEG_LEN_DHT_COLOR_LO 0xa2
> +#define JPEG_LEN_SOS_MONO_LO 0x08
> +#define JPEG_LEN_SOS_COLOR_LO 0x0c
> +
> +struct jpeg_header_buf {
> + u8 *ptr;
> + u32 size;
> + u32 pos;
> +};
> +
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 13:02 ` Vladimir Zapolskiy
@ 2026-07-06 13:37 ` Bryan O'Donoghue
2026-07-06 13:57 ` Vladimir Zapolskiy
0 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 13:37 UTC (permalink / raw)
To: Vladimir Zapolskiy, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 06/07/2026 14:02, Vladimir Zapolskiy wrote:
>>>
>>> Why? The common power domain and interconnects have already been
>>> described as resources of the parent CAMSS device, there is no need
>>> to duplicate descriptions in every child device tree node of CAMSS.
>>
>> CSID, IFE etc are not parents of the JPEG encoder.
>>
>> The idea is to make all of these peers, eventually. Not particular block
>> owns the TITAN_TOP_GDSC - the entire CAMSS block is a collapsible block.
>>
>
> The statement provides the same information as before, e.g. it's found
> here:
>
> https://lore.kernel.org/linux-media/fbc018f5-
> c025-4747-85f2-53b45b0f0496@nxsw.ie/
>
> There's no functional linkage between CAMSS/IFE and JPEG - they are
> peers within the CAMSS power-island.
>
> Thus "the CAMSS power-island" exists, and it got its hardware description
> as the CAMSS top device tree node, and TITAN_TOP_GDSC power domain is
> a natural resource of all devices on "CAMSS power-island",
Vlad honestly, TITAN_TOP_GDSC is provided to the CAMSS island - all of
the nodes within it depend on that and it is _external_ to the block.
There is a CSID MUX on some SOC other than that I'll restate it again,
I'm not sure why you won't accept it.
There is no dependency between JPEG and IFE, one is _not_ subordinate to
the other and trying to find ways to invalidate that statement runs
counter to the facts.
CAMCC provides TITAN_TOP_GDSC external to the CAMSS block, that GDSC
effectively _is_ the power island.
This debate is going nowhere. Check the downstream you don't need the
docs titan_top_gdsc is provided by CAMCC.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 13:37 ` Bryan O'Donoghue
@ 2026-07-06 13:57 ` Vladimir Zapolskiy
2026-07-06 14:12 ` Bryan O'Donoghue
0 siblings, 1 reply; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 13:57 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 7/6/26 16:37, Bryan O'Donoghue wrote:
> On 06/07/2026 14:02, Vladimir Zapolskiy wrote:
>>>>
>>>> Why? The common power domain and interconnects have already been
>>>> described as resources of the parent CAMSS device, there is no need
>>>> to duplicate descriptions in every child device tree node of CAMSS.
>>>
>>> CSID, IFE etc are not parents of the JPEG encoder.
>>>
>>> The idea is to make all of these peers, eventually. Not particular block
>>> owns the TITAN_TOP_GDSC - the entire CAMSS block is a collapsible block.
>>>
>>
>> The statement provides the same information as before, e.g. it's found
>> here:
>>
>> https://lore.kernel.org/linux-media/fbc018f5-
>> c025-4747-85f2-53b45b0f0496@nxsw.ie/
>>
>> There's no functional linkage between CAMSS/IFE and JPEG - they are
>> peers within the CAMSS power-island.
>>
>> Thus "the CAMSS power-island" exists, and it got its hardware description
>> as the CAMSS top device tree node, and TITAN_TOP_GDSC power domain is
>> a natural resource of all devices on "CAMSS power-island",
>
> Vlad honestly, TITAN_TOP_GDSC is provided to the CAMSS island - all of
> the nodes within it depend on that and it is _external_ to the block.
So? There is no tension with my point of view.
> There is a CSID MUX on some SOC other than that I'll restate it again,
> I'm not sure why you won't accept it.
>
> There is no dependency between JPEG and IFE, one is _not_ subordinate to
> the other and trying to find ways to invalidate that statement runs
> counter to the facts.
>
> CAMCC provides TITAN_TOP_GDSC external to the CAMSS block, that GDSC
> effectively _is_ the power island.
>
> This debate is going nowhere. Check the downstream you don't need the
> docs titan_top_gdsc is provided by CAMCC.
>
There is no debate, since my arguments are not debated so far... Why?
And at the same time you didn't provide any support to have N identical
spread hardware property descriptions instead of the single one.
What is a good enough reason to prefer complexity over simplicity?
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 13:57 ` Vladimir Zapolskiy
@ 2026-07-06 14:12 ` Bryan O'Donoghue
2026-07-06 14:35 ` Vladimir Zapolskiy
0 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 14:12 UTC (permalink / raw)
To: Vladimir Zapolskiy, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 06/07/2026 14:57, Vladimir Zapolskiy wrote:
>> This debate is going nowhere. Check the downstream you don't need the
>> docs titan_top_gdsc is provided by CAMCC.
>>
>
> There is no debate, since my arguments are not debated so far... Why?
An odd claim, since this is the fourth ? level of email reply.
> And at the same time you didn't provide any support to have N identical
> spread hardware property descriptions instead of the single one.
>
> What is a good enough reason to prefer complexity over simplicity?
I'm not sure I parse your here. The core fact is there is no "thing" in
the CAMSS block that can be modelled as a provider to JPEG in lieu of
TITAN_TOP_GDSC.
That GDSC is provided by CAMCC not by any other silicon block. So
omitting power-domains for individual nodes is incorrect. Best practice
and in fact the _only_ practice that can work is to list power-domains,
interconnects in the individual nodes themselves as - I've pointed out
multiple times now - those blocks have to ramp clocks and scale voltages
dependent on their particular use cases.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 14:12 ` Bryan O'Donoghue
@ 2026-07-06 14:35 ` Vladimir Zapolskiy
2026-07-06 14:49 ` Bryan O'Donoghue
0 siblings, 1 reply; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 14:35 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 7/6/26 17:12, Bryan O'Donoghue wrote:
> On 06/07/2026 14:57, Vladimir Zapolskiy wrote:
>>> This debate is going nowhere. Check the downstream you don't need the
>>> docs titan_top_gdsc is provided by CAMCC.
>>>
>>
>> There is no debate, since my arguments are not debated so far... Why?
>
> An odd claim, since this is the fourth ? level of email reply.
>
>> And at the same time you didn't provide any support to have N identical
>> spread hardware property descriptions instead of the single one.
>>
>> What is a good enough reason to prefer complexity over simplicity?
>
> I'm not sure I parse your here. The core fact is there is no "thing" in
> the CAMSS block that can be modelled as a provider to JPEG in lieu of
> TITAN_TOP_GDSC.
That's fine, I have never said the opposite.
> That GDSC is provided by CAMCC not by any other silicon block. So
That's fine.
> omitting power-domains for individual nodes is incorrect. Best practice
> and in fact the _only_ practice that can work is to list power-domains,
Let's step back and discuss hardware description of CAMSS IPs, so far
there is no point to jump to the practice.
> interconnects in the individual nodes themselves as - I've pointed out
> multiple times now - those blocks have to ramp clocks and scale voltages
> dependent on their particular use cases.
>
Right, you've pointed it a few times, no surprise a straw man fallacy
argument resembles as is, because it does not address or debate the
initial point of concern [1], when the absolutely excessive complexity
is proposed to be implemented for "CAMSS power island" and its IPs.
[1] https://lore.kernel.org/linux-media/e04cb9dc-76dd-4fa8-92f4-be002bf7ee8b@linaro.org
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 14:35 ` Vladimir Zapolskiy
@ 2026-07-06 14:49 ` Bryan O'Donoghue
2026-07-06 15:00 ` Vladimir Zapolskiy
0 siblings, 1 reply; 33+ messages in thread
From: Bryan O'Donoghue @ 2026-07-06 14:49 UTC (permalink / raw)
To: Vladimir Zapolskiy, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 06/07/2026 15:35, Vladimir Zapolskiy wrote:
>> omitting power-domains for individual nodes is incorrect. Best practice
>> and in fact the _only_ practice that can work is to list power-domains,
>
> Let's step back and discuss hardware description of CAMSS IPs, so far
> there is no point to jump to the practice.
>
>> interconnects in the individual nodes themselves as - I've pointed out
>> multiple times now - those blocks have to ramp clocks and scale voltages
>> dependent on their particular use cases.
>>
>
> Right, you've pointed it a few times, no surprise a straw man fallacy
> argument resembles as is, because it does not address or debate the
> initial point of concern [1], when the absolutely excessive complexity
> is proposed to be implemented for "CAMSS power island" and its IPs.
>
> [1] https://lore.kernel.org/linux-media/e04cb9dc-76dd-4fa8-92f4-
> be002bf7ee8b@linaro.org
You seem a little stuck on the term "CAMSS power island" the individual
blocks within the CAMSS "island" are individually collapsible.
That is why they _must_ have their own power-domains.
The CamNoc is shared between these components but not outside. Hence it
is more like a bus than a parent/child relationship.
As to your argument about complexity. I think that's subjective.
---
bod
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250
2026-07-06 14:49 ` Bryan O'Donoghue
@ 2026-07-06 15:00 ` Vladimir Zapolskiy
0 siblings, 0 replies; 33+ messages in thread
From: Vladimir Zapolskiy @ 2026-07-06 15:00 UTC (permalink / raw)
To: Bryan O'Donoghue, Atanas Filipov, linux-media
Cc: loic.poulain, mchehab, robh, krzk+dt, conor+dt, andersson,
konradybcio, linux-arm-msm, linux-kernel, devicetree
On 7/6/26 17:49, Bryan O'Donoghue wrote:
> On 06/07/2026 15:35, Vladimir Zapolskiy wrote:
>>> omitting power-domains for individual nodes is incorrect. Best practice
>>> and in fact the _only_ practice that can work is to list power-domains,
>>
>> Let's step back and discuss hardware description of CAMSS IPs, so far
>> there is no point to jump to the practice.
>>
>>> interconnects in the individual nodes themselves as - I've pointed out
>>> multiple times now - those blocks have to ramp clocks and scale voltages
>>> dependent on their particular use cases.
>>>
>>
>> Right, you've pointed it a few times, no surprise a straw man fallacy
>> argument resembles as is, because it does not address or debate the
>> initial point of concern [1], when the absolutely excessive complexity
>> is proposed to be implemented for "CAMSS power island" and its IPs.
>>
>> [1] https://lore.kernel.org/linux-media/e04cb9dc-76dd-4fa8-92f4-
>> be002bf7ee8b@linaro.org
>
> You seem a little stuck on the term "CAMSS power island" the individual
> blocks within the CAMSS "island" are individually collapsible.
It's not me, who stuck, but dt bindings documentation describes CAMSS on
a number of Qualcomm platforms.
> That is why they _must_ have their own power-domains.
>
> The CamNoc is shared between these components but not outside. Hence it
> is more like a bus than a parent/child relationship.
>
> As to your argument about complexity. I think that's subjective.
>
What is subjective between one instance and multiple identical instances
of hardware properties? The first is simple the latter is complex.
--
Best wishes,
Vladimir
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
` (3 preceding siblings ...)
2026-07-06 13:11 ` Dmitry Baryshkov
@ 2026-07-06 17:13 ` Uwe Kleine-König
4 siblings, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2026-07-06 17:13 UTC (permalink / raw)
To: Atanas Filipov
Cc: linux-media, bryan.odonoghue, vladimir.zapolskiy, loic.poulain,
mchehab, robh, krzk+dt, conor+dt, andersson, konradybcio,
linux-arm-msm, linux-kernel, devicetree
[-- Attachment #1: Type: text/plain, Size: 231 bytes --]
Hello,
On Mon, Jul 06, 2026 at 10:11:13AM +0300, Atanas Filipov wrote:
> +#include <linux/mod_devicetable.h>
Please don't add new users of this header. Only use the
<linux/device-id/*.h> that you actually need.
Best regards
Uwe
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 33+ messages in thread
end of thread, other threads:[~2026-07-06 17:13 UTC | newest]
Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 7:11 [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Atanas Filipov
2026-07-06 7:11 ` [PATCH v4 1/5] media: qcom: camss: Populate CAMSS child devices via DT Atanas Filipov
2026-07-06 12:09 ` Krzysztof Kozlowski
2026-07-06 12:27 ` Bryan O'Donoghue
2026-07-06 12:36 ` Krzysztof Kozlowski
2026-07-06 12:38 ` Bryan O'Donoghue
2026-07-06 12:41 ` Dmitry Baryshkov
2026-07-06 7:11 ` [PATCH v4 2/5] dt-bindings: media: qcom,sm8250-camss: allow JPEG encoder child node Atanas Filipov
2026-07-06 10:07 ` Bryan O'Donoghue
2026-07-06 12:11 ` Krzysztof Kozlowski
2026-07-06 7:11 ` [PATCH v4 3/5] dt-bindings: media: qcom: Add JPEG encoder binding Atanas Filipov
2026-07-06 10:19 ` Bryan O'Donoghue
2026-07-06 12:17 ` Krzysztof Kozlowski
2026-07-06 7:11 ` [PATCH v4 4/5] arm64: dts: qcom: sm8250: Add JPEG encoder node Atanas Filipov
2026-07-06 12:18 ` Krzysztof Kozlowski
2026-07-06 12:45 ` Dmitry Baryshkov
2026-07-06 7:11 ` [PATCH v4 5/5] media: qcom: jpeg: Add Qualcomm JPEG V4L2 encoder Atanas Filipov
2026-07-06 7:46 ` Vladimir Zapolskiy
2026-07-06 10:21 ` Bryan O'Donoghue
2026-07-06 12:19 ` Krzysztof Kozlowski
2026-07-06 13:11 ` Dmitry Baryshkov
2026-07-06 17:13 ` Uwe Kleine-König
2026-07-06 10:12 ` [PATCH v4 0/5] Add Qualcomm JPEG V4L2 encoder for SM8250 Bryan O'Donoghue
2026-07-06 12:00 ` Vladimir Zapolskiy
2026-07-06 12:11 ` Bryan O'Donoghue
2026-07-06 13:02 ` Vladimir Zapolskiy
2026-07-06 13:37 ` Bryan O'Donoghue
2026-07-06 13:57 ` Vladimir Zapolskiy
2026-07-06 14:12 ` Bryan O'Donoghue
2026-07-06 14:35 ` Vladimir Zapolskiy
2026-07-06 14:49 ` Bryan O'Donoghue
2026-07-06 15:00 ` Vladimir Zapolskiy
2026-07-06 12:24 ` Krzysztof Kozlowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox