* [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30
@ 2025-09-25 15:16 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114 Svyatoslav Ryhel
` (21 more replies)
0 siblings, 22 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Add support for MIPI CSI device found in Tegra20 and Tegra30 SoC along
with a set of changes required for that.
---
Changes in v2:
- vi_sensor gated through csus
- TEGRA30_CLK_CLK_MAX moved to clk-tegra30
- adjusted commit titles and messages
- clk_register_clkdev dropped from pad clock registration
- removed tegra30-vi/vip and used tegra20 fallback
- added separate csi schema for tegra20-csi and tegra30-csi
- fixet number of VI channels
- adjusted tegra_vi_out naming
- fixed yuv_input_format to main_input_format
- MIPI calibration refsctored for Tegra114+ and added support for
pre-Tegra114 to use CSI as a MIPI calibration device
- switched ENOMEM to EBUSY
- added check into tegra_channel_get_remote_csi_subdev
- moved avdd-dsi-csi-supply into CSI
- next_fs_sp_idx > next_fs_sp_value
- removed host1x_syncpt_incr from framecounted syncpoint
- csi subdev request moved before frame cycle
Changes in v3:
- tegra20 and tegra30 csi schema merged
- removed unneeded properties and requirements from schema
- improved vendor specific properties description
- added tegra20 csus parent mux
- improved commit descriptions
- redesigned MIPI-calibration to expose less SoC related data into header
- commit "staging: media: tegra-video: csi: add support for SoCs with integrated
MIPI calibration" dropped as unneeded
- improved tegra_channel_get_remote_device_subdev logic
- avdd-dsi-csi-supply moved from vi to csi for p2597 and p3450-0000
- software syncpoint counters switched to direct reading
- adjusted planar formats offset calculation
---
Svyatoslav Ryhel (22):
clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and
Tegra114
dt-bindings: clock: tegra30: Add IDs for CSI pad clocks
clk: tegra30: add CSI pad clock gates
dt-bindings: display: tegra: document Tegra30 VI and VIP
staging: media: tegra-video: expand VI and VIP support to Tegra30
staging: media: tegra-video: vi: adjust get_selection op check
staging: media: tegra-video: vi: add flip controls only if no source
controls are provided
staging: media: tegra-video: csi: move CSI helpers to header
gpu: host1x: convert MIPI to use operation function pointers
staging: media: tegra-video: vi: improve logic of source requesting
staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to
CSI
arm64: tegra: move avdd-dsi-csi-supply into CSI node
staging: media: tegra-video: tegra20: set correct maximum width and
height
staging: media: tegra-video: tegra20: add support for second output of
VI
staging: media: tegra-video: tegra20: simplify format align
calculations
staging: media: tegra-video: tegra20: set VI HW revision
staging: media: tegra-video: tegra20: increase maximum VI clock
frequency
staging: media: tegra-video: tegra20: expand format support with
RAW8/10 and YUV422 1X16
staging: media: tegra-video: tegra20: adjust luma buffer stride
dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
ARM: tegra: add CSI nodes for Tegra20 and Tegra30
staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
.../display/tegra/nvidia,tegra20-csi.yaml | 135 +++
.../display/tegra/nvidia,tegra20-vi.yaml | 19 +-
.../display/tegra/nvidia,tegra20-vip.yaml | 9 +-
arch/arm/boot/dts/nvidia/tegra20.dtsi | 19 +-
arch/arm/boot/dts/nvidia/tegra30.dtsi | 24 +-
.../arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 4 +-
.../boot/dts/nvidia/tegra210-p3450-0000.dts | 4 +-
drivers/clk/tegra/clk-tegra114.c | 7 +-
drivers/clk/tegra/clk-tegra20.c | 20 +-
drivers/clk/tegra/clk-tegra30.c | 21 +-
drivers/gpu/drm/tegra/dsi.c | 1 +
drivers/gpu/host1x/Makefile | 1 +
drivers/gpu/host1x/dev.c | 2 +
drivers/gpu/host1x/dev.h | 2 +
drivers/gpu/host1x/mipi.c | 501 +----------
drivers/gpu/host1x/tegra114-mipi.c | 483 ++++++++++
drivers/pinctrl/tegra/pinctrl-tegra20.c | 7 +
drivers/staging/media/tegra-video/Makefile | 1 +
drivers/staging/media/tegra-video/csi.c | 66 +-
drivers/staging/media/tegra-video/csi.h | 16 +
drivers/staging/media/tegra-video/tegra20.c | 828 +++++++++++++++---
drivers/staging/media/tegra-video/vi.c | 56 +-
drivers/staging/media/tegra-video/vi.h | 9 +-
drivers/staging/media/tegra-video/video.c | 8 +-
drivers/staging/media/tegra-video/vip.c | 4 +-
include/dt-bindings/clock/tegra30-car.h | 3 +-
include/linux/host1x.h | 10 -
include/linux/tegra-mipi-cal.h | 56 ++
28 files changed, 1648 insertions(+), 668 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
create mode 100644 drivers/gpu/host1x/tegra114-mipi.c
create mode 100644 include/linux/tegra-mipi-cal.h
--
2.48.1
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-01 4:02 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 02/22] dt-bindings: clock: tegra30: Add IDs for CSI pad clocks Svyatoslav Ryhel
` (20 subsequent siblings)
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
The CSUS clock is a clock gate for the output clock signal primarily
sourced from the VI_SENSOR clock. This clock signal is used as an input
MCLK clock for cameras.
Unlike later Tegra SoCs, the Tegra 20 can change its CSUS parent, which is
why csus_mux is added in a similar way to how CDEV1 and CDEV2 are handled.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/clk/tegra/clk-tegra114.c | 7 ++++++-
drivers/clk/tegra/clk-tegra20.c | 20 +++++++++++++-------
drivers/clk/tegra/clk-tegra30.c | 7 ++++++-
drivers/pinctrl/tegra/pinctrl-tegra20.c | 7 +++++++
4 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index 186b0b81c1ec..00282b0d3763 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -691,7 +691,6 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = {
[tegra_clk_tsec] = { .dt_id = TEGRA114_CLK_TSEC, .present = true },
[tegra_clk_xusb_host] = { .dt_id = TEGRA114_CLK_XUSB_HOST, .present = true },
[tegra_clk_msenc] = { .dt_id = TEGRA114_CLK_MSENC, .present = true },
- [tegra_clk_csus] = { .dt_id = TEGRA114_CLK_CSUS, .present = true },
[tegra_clk_mselect] = { .dt_id = TEGRA114_CLK_MSELECT, .present = true },
[tegra_clk_tsensor] = { .dt_id = TEGRA114_CLK_TSENSOR, .present = true },
[tegra_clk_i2s3] = { .dt_id = TEGRA114_CLK_I2S3, .present = true },
@@ -1047,6 +1046,12 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base,
0, 82, periph_clk_enb_refcnt);
clks[TEGRA114_CLK_DSIB] = clk;
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
+ clk_base, 0, TEGRA114_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA114_CLK_CSUS] = clk;
+
/* emc mux */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
ARRAY_SIZE(mux_pllmcp_clkm),
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index 2c58ce25af75..d8d5afeb6f9b 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -530,7 +530,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
[tegra_clk_rtc] = { .dt_id = TEGRA20_CLK_RTC, .present = true },
[tegra_clk_timer] = { .dt_id = TEGRA20_CLK_TIMER, .present = true },
[tegra_clk_kbc] = { .dt_id = TEGRA20_CLK_KBC, .present = true },
- [tegra_clk_csus] = { .dt_id = TEGRA20_CLK_CSUS, .present = true },
[tegra_clk_vcp] = { .dt_id = TEGRA20_CLK_VCP, .present = true },
[tegra_clk_bsea] = { .dt_id = TEGRA20_CLK_BSEA, .present = true },
[tegra_clk_bsev] = { .dt_id = TEGRA20_CLK_BSEV, .present = true },
@@ -834,6 +833,12 @@ static void __init tegra20_periph_clk_init(void)
clk_base, 0, 93, periph_clk_enb_refcnt);
clks[TEGRA20_CLK_CDEV2] = clk;
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "csus_mux", 0,
+ clk_base, 0, TEGRA20_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA20_CLK_CSUS] = clk;
+
for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
data = &tegra_periph_clk_list[i];
clk = tegra_clk_register_periph_data(clk_base, data);
@@ -1093,14 +1098,15 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
hw = __clk_get_hw(clk);
/*
- * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
- * clock is created by the pinctrl driver. It is possible for clk user
- * to request these clocks before pinctrl driver got probed and hence
- * user will get an orphaned clock. That might be undesirable because
- * user may expect parent clock to be enabled by the child.
+ * Tegra20 CDEV1, CDEV2 and CSUS clocks are a bit special case, their
+ * parent clock is created by the pinctrl driver. It is possible for
+ * clk user to request these clocks before pinctrl driver got probed
+ * and hence user will get an orphaned clock. That might be undesirable
+ * because user may expect parent clock to be enabled by the child.
*/
if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
- clkspec->args[0] == TEGRA20_CLK_CDEV2) {
+ clkspec->args[0] == TEGRA20_CLK_CDEV2 ||
+ clkspec->args[0] == TEGRA20_CLK_CSUS) {
parent_hw = clk_hw_get_parent(hw);
if (!parent_hw)
return ERR_PTR(-EPROBE_DEFER);
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index 82a8cb9545eb..ca367184e185 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -779,7 +779,6 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
[tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true },
[tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },
[tegra_clk_kbc] = { .dt_id = TEGRA30_CLK_KBC, .present = true },
- [tegra_clk_csus] = { .dt_id = TEGRA30_CLK_CSUS, .present = true },
[tegra_clk_vcp] = { .dt_id = TEGRA30_CLK_VCP, .present = true },
[tegra_clk_bsea] = { .dt_id = TEGRA30_CLK_BSEA, .present = true },
[tegra_clk_bsev] = { .dt_id = TEGRA30_CLK_BSEV, .present = true },
@@ -1008,6 +1007,12 @@ static void __init tegra30_periph_clk_init(void)
0, 48, periph_clk_enb_refcnt);
clks[TEGRA30_CLK_DSIA] = clk;
+ /* csus */
+ clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
+ clk_base, 0, TEGRA30_CLK_CSUS,
+ periph_clk_enb_refcnt);
+ clks[TEGRA30_CLK_CSUS] = clk;
+
/* pcie */
clk = tegra_clk_register_periph_gate("pcie", "clk_m", 0, clk_base, 0,
70, periph_clk_enb_refcnt);
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c
index 737fc2000f66..437e0ac091cc 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra20.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c
@@ -2230,6 +2230,10 @@ static const char *cdev2_parents[] = {
"dev2_osc_div", "hclk", "pclk", "pll_p_out4",
};
+static const char *csus_parents[] = {
+ "pll_c_out1", "pll_p_out2", "pll_p_out3", "vi_sensor",
+};
+
static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev)
{
struct tegra_pmx *pmx = platform_get_drvdata(pdev);
@@ -2239,6 +2243,9 @@ static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev)
clk_register_mux(NULL, "cdev2_mux", cdev2_parents, 4, 0,
pmx->regs[1] + 0x8, 4, 2, CLK_MUX_READ_ONLY, NULL);
+
+ clk_register_mux(NULL, "csus_mux", csus_parents, 4, 0,
+ pmx->regs[1] + 0x8, 6, 2, CLK_MUX_READ_ONLY, NULL);
}
static int tegra20_pinctrl_probe(struct platform_device *pdev)
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 02/22] dt-bindings: clock: tegra30: Add IDs for CSI pad clocks
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114 Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 03/22] clk: tegra30: add CSI pad clock gates Svyatoslav Ryhel
` (19 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Tegra30 has CSI pad clock enable bits embedded into PLLD/PLLD2 registers.
Add ids for these clocks. Additionally, move TEGRA30_CLK_CLK_MAX into
clk-tegra30 source.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
drivers/clk/tegra/clk-tegra30.c | 1 +
include/dt-bindings/clock/tegra30-car.h | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index ca367184e185..ca738bc64615 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -53,6 +53,7 @@
#define SYSTEM_CLK_RATE 0x030
#define TEGRA30_CLK_PERIPH_BANKS 5
+#define TEGRA30_CLK_CLK_MAX 311
#define PLLC_BASE 0x80
#define PLLC_MISC 0x8c
diff --git a/include/dt-bindings/clock/tegra30-car.h b/include/dt-bindings/clock/tegra30-car.h
index f193663e6f28..763b81f80908 100644
--- a/include/dt-bindings/clock/tegra30-car.h
+++ b/include/dt-bindings/clock/tegra30-car.h
@@ -271,6 +271,7 @@
#define TEGRA30_CLK_AUDIO3_MUX 306
#define TEGRA30_CLK_AUDIO4_MUX 307
#define TEGRA30_CLK_SPDIF_MUX 308
-#define TEGRA30_CLK_CLK_MAX 309
+#define TEGRA30_CLK_CSIA_PAD 309
+#define TEGRA30_CLK_CSIB_PAD 310
#endif /* _DT_BINDINGS_CLOCK_TEGRA30_CAR_H */
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 03/22] clk: tegra30: add CSI pad clock gates
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 02/22] dt-bindings: clock: tegra30: Add IDs for CSI pad clocks Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP Svyatoslav Ryhel
` (18 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Tegra30 has CSI pad bits in both PLLD and PLLD2 clocks that are required
for the correct work of the CSI block. Add CSI pad A and pad B clock gates
with PLLD/PLLD2 parents, respectively. Add a plld2 spinlock, like one plld
uses, to prevent simultaneous access since both the PLLDx and CSIx_PAD
clocks use the same registers
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/clk/tegra/clk-tegra30.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index ca738bc64615..61fe527ee6c1 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -154,6 +154,7 @@ static unsigned long input_freq;
static DEFINE_SPINLOCK(cml_lock);
static DEFINE_SPINLOCK(pll_d_lock);
+static DEFINE_SPINLOCK(pll_d2_lock);
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
_clk_num, _gate_flags, _clk_id) \
@@ -859,7 +860,7 @@ static void __init tegra30_pll_init(void)
/* PLLD2 */
clk = tegra_clk_register_pll("pll_d2", "pll_ref", clk_base, pmc_base, 0,
- &pll_d2_params, NULL);
+ &pll_d2_params, &pll_d2_lock);
clks[TEGRA30_CLK_PLL_D2] = clk;
/* PLLD2_OUT0 */
@@ -1008,6 +1009,16 @@ static void __init tegra30_periph_clk_init(void)
0, 48, periph_clk_enb_refcnt);
clks[TEGRA30_CLK_DSIA] = clk;
+ /* csia_pad */
+ clk = clk_register_gate(NULL, "csia_pad", "pll_d", CLK_SET_RATE_PARENT,
+ clk_base + PLLD_BASE, 26, 0, &pll_d_lock);
+ clks[TEGRA30_CLK_CSIA_PAD] = clk;
+
+ /* csib_pad */
+ clk = clk_register_gate(NULL, "csib_pad", "pll_d2", CLK_SET_RATE_PARENT,
+ clk_base + PLLD2_BASE, 26, 0, &pll_d2_lock);
+ clks[TEGRA30_CLK_CSIB_PAD] = clk;
+
/* csus */
clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
clk_base, 0, TEGRA30_CLK_CSUS,
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (2 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 03/22] clk: tegra30: add CSI pad clock gates Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-02 1:19 ` Rob Herring (Arm)
2025-09-25 15:16 ` [PATCH v3 05/22] staging: media: tegra-video: expand VI and VIP support to Tegra30 Svyatoslav Ryhel
` (17 subsequent siblings)
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Existing Parallel VI interface schema for Tegra20 is fully compatible with
Tegra30; hence, lets reuse it by setting fallback for Tegra30.
Adjust existing VI schema to reflect that Tegra20 VI is compatible with
Tegra30 by setting a fallback for Tegra30. Additionally, switch to using
an enum instead of list of const.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../display/tegra/nvidia,tegra20-vi.yaml | 19 ++++++++++++-------
.../display/tegra/nvidia,tegra20-vip.yaml | 9 +++++++--
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vi.yaml
index 2181855a0920..dd67d4162884 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vi.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vi.yaml
@@ -16,16 +16,21 @@ properties:
compatible:
oneOf:
- - const: nvidia,tegra20-vi
- - const: nvidia,tegra30-vi
- - const: nvidia,tegra114-vi
- - const: nvidia,tegra124-vi
+ - enum:
+ - nvidia,tegra20-vi
+ - nvidia,tegra114-vi
+ - nvidia,tegra124-vi
+ - nvidia,tegra210-vi
+ - nvidia,tegra186-vi
+ - nvidia,tegra194-vi
+
+ - items:
+ - const: nvidia,tegra30-vi
+ - const: nvidia,tegra20-vi
+
- items:
- const: nvidia,tegra132-vi
- const: nvidia,tegra124-vi
- - const: nvidia,tegra210-vi
- - const: nvidia,tegra186-vi
- - const: nvidia,tegra194-vi
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vip.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vip.yaml
index 14294edb8d8c..9104a36e16d9 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vip.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vip.yaml
@@ -11,8 +11,13 @@ maintainers:
properties:
compatible:
- enum:
- - nvidia,tegra20-vip
+ oneOf:
+ - enum:
+ - nvidia,tegra20-vip
+
+ - items:
+ - const: nvidia,tegra30-vip
+ - const: nvidia,tegra20-vip
ports:
$ref: /schemas/graph.yaml#/properties/ports
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 05/22] staging: media: tegra-video: expand VI and VIP support to Tegra30
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (3 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 06/22] staging: media: tegra-video: vi: adjust get_selection op check Svyatoslav Ryhel
` (16 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Existing VI and VIP implementation for Tegra20 is fully compatible with
Tegra30.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com> # Tegra20 VIP
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
drivers/staging/media/tegra-video/Makefile | 1 +
drivers/staging/media/tegra-video/vi.c | 2 +-
drivers/staging/media/tegra-video/vi.h | 2 +-
drivers/staging/media/tegra-video/video.c | 2 +-
drivers/staging/media/tegra-video/vip.c | 4 ++--
5 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/staging/media/tegra-video/Makefile b/drivers/staging/media/tegra-video/Makefile
index 6c7552e05109..96380b5dbd8b 100644
--- a/drivers/staging/media/tegra-video/Makefile
+++ b/drivers/staging/media/tegra-video/Makefile
@@ -6,5 +6,6 @@ tegra-video-objs := \
csi.o
tegra-video-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o
+tegra-video-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra20.o
tegra-video-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video.o
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index c9276ff76157..7c44a3448588 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1956,7 +1956,7 @@ static void tegra_vi_remove(struct platform_device *pdev)
}
static const struct of_device_id tegra_vi_of_id_table[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vi", .data = &tegra20_vi_soc },
#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
index 1e6a5caa7082..cac0c0d0e225 100644
--- a/drivers/staging/media/tegra-video/vi.h
+++ b/drivers/staging/media/tegra-video/vi.h
@@ -296,7 +296,7 @@ struct tegra_video_format {
u32 fourcc;
};
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
extern const struct tegra_vi_soc tegra20_vi_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
index 074ad0dc56ca..6fe8d5301b9c 100644
--- a/drivers/staging/media/tegra-video/video.c
+++ b/drivers/staging/media/tegra-video/video.c
@@ -123,7 +123,7 @@ static int host1x_video_remove(struct host1x_device *dev)
}
static const struct of_device_id host1x_video_subdevs[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vip", },
{ .compatible = "nvidia,tegra20-vi", },
#endif
diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c
index 5ec717f3afd5..34397b73bb61 100644
--- a/drivers/staging/media/tegra-video/vip.c
+++ b/drivers/staging/media/tegra-video/vip.c
@@ -263,12 +263,12 @@ static void tegra_vip_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
extern const struct tegra_vip_soc tegra20_vip_soc;
#endif
static const struct of_device_id tegra_vip_of_id_table[] = {
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
{ .compatible = "nvidia,tegra20-vip", .data = &tegra20_vip_soc },
#endif
{ }
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 06/22] staging: media: tegra-video: vi: adjust get_selection op check
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (4 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 05/22] staging: media: tegra-video: expand VI and VIP support to Tegra30 Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 07/22] staging: media: tegra-video: vi: add flip controls only if no source controls are provided Svyatoslav Ryhel
` (15 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Get_selection operation may be implemented only for sink pad and may
return error code. Set try_crop to 0 instead of returning error.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/vi.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 7c44a3448588..856b7c18b551 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -476,15 +476,11 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
fse.code = fmtinfo->code;
ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse);
if (ret) {
- if (!v4l2_subdev_has_op(subdev, pad, get_selection)) {
+ if (!v4l2_subdev_has_op(subdev, pad, get_selection) ||
+ v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel)) {
try_crop->width = 0;
try_crop->height = 0;
} else {
- ret = v4l2_subdev_call(subdev, pad, get_selection,
- NULL, &sdsel);
- if (ret)
- return -EINVAL;
-
try_crop->width = sdsel.r.width;
try_crop->height = sdsel.r.height;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 07/22] staging: media: tegra-video: vi: add flip controls only if no source controls are provided
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (5 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 06/22] staging: media: tegra-video: vi: adjust get_selection op check Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 08/22] staging: media: tegra-video: csi: move CSI helpers to header Svyatoslav Ryhel
` (14 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Because the current Tegra video driver is video-centric, it exposes all
controls via /dev/video. If both the camera sensor and the VI provide
hflip and vflip, the driver will fail because only one control is allowed.
To address this, hflip and vflip should be added from the SoC only if the
camera sensor doesn't provide those controls.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/vi.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 856b7c18b551..90473729b546 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -961,6 +961,7 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan)
}
#else
struct v4l2_subdev *subdev;
+ struct v4l2_ctrl *hflip, *vflip;
/* custom control */
v4l2_ctrl_new_custom(&chan->ctrl_handler, &syncpt_timeout_ctrl, NULL);
@@ -986,11 +987,13 @@ static int tegra_channel_setup_ctrl_handler(struct tegra_vi_channel *chan)
return ret;
}
- if (chan->vi->soc->has_h_v_flip) {
+ hflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_HFLIP);
+ if (chan->vi->soc->has_h_v_flip && !hflip)
v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- }
+ vflip = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_VFLIP);
+ if (chan->vi->soc->has_h_v_flip && !vflip)
+ v4l2_ctrl_new_std(&chan->ctrl_handler, &vi_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
#endif
/* setup the controls */
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 08/22] staging: media: tegra-video: csi: move CSI helpers to header
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (6 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 07/22] staging: media: tegra-video: vi: add flip controls only if no source controls are provided Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers Svyatoslav Ryhel
` (13 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Move CSI helpers into the header for easier access from SoC-specific video
driver parts.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/csi.c | 11 -----------
drivers/staging/media/tegra-video/csi.h | 10 ++++++++++
2 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 604185c00a1a..74c92db1032f 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -20,17 +20,6 @@
#define MHZ 1000000
-static inline struct tegra_csi *
-host1x_client_to_csi(struct host1x_client *client)
-{
- return container_of(client, struct tegra_csi, client);
-}
-
-static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev)
-{
- return container_of(subdev, struct tegra_csi_channel, subdev);
-}
-
/*
* CSI is a separate subdevice which has 6 source pads to generate
* test pattern. CSI subdevice pad ops are used only for TPG and
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 3e6e5ee1bb1e..3ed2dbc73ce9 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -151,6 +151,16 @@ struct tegra_csi {
struct list_head csi_chans;
};
+static inline struct tegra_csi *host1x_client_to_csi(struct host1x_client *client)
+{
+ return container_of(client, struct tegra_csi, client);
+}
+
+static inline struct tegra_csi_channel *to_csi_chan(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct tegra_csi_channel, subdev);
+}
+
void tegra_csi_error_recover(struct v4l2_subdev *subdev);
void tegra_csi_calc_settle_time(struct tegra_csi_channel *csi_chan,
u8 csi_port_num,
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (7 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 08/22] staging: media: tegra-video: csi: move CSI helpers to header Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-01 4:18 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 10/22] staging: media: tegra-video: vi: improve logic of source requesting Svyatoslav Ryhel
` (12 subsequent siblings)
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Convert existing MIPI code to use operation function pointers, a necessary
step for supporting Tegra20/Tegra30 SoCs. All common MIPI configuration
that is SoC-independent remains in mipi.c, while all SoC-specific code is
moved to tegra114-mipi.c (The naming matches the first SoC generation with
a dedicated calibration block). Shared structures and function calls are
placed into tegra-mipi-cal.h.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/gpu/drm/tegra/dsi.c | 1 +
drivers/gpu/host1x/Makefile | 1 +
drivers/gpu/host1x/dev.c | 2 +
drivers/gpu/host1x/dev.h | 2 +
drivers/gpu/host1x/mipi.c | 501 +++---------------------
drivers/gpu/host1x/tegra114-mipi.c | 483 +++++++++++++++++++++++
drivers/staging/media/tegra-video/csi.c | 1 +
include/linux/host1x.h | 10 -
include/linux/tegra-mipi-cal.h | 56 +++
9 files changed, 594 insertions(+), 463 deletions(-)
create mode 100644 drivers/gpu/host1x/tegra114-mipi.c
create mode 100644 include/linux/tegra-mipi-cal.h
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 64f12a85a9dd..278bf2c85524 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -14,6 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/tegra-mipi-cal.h>
#include <video/mipi_display.h>
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index ee5286ffe08d..fead483af0b4 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -9,6 +9,7 @@ host1x-y = \
job.o \
debug.o \
mipi.o \
+ tegra114-mipi.o \
fence.o \
hw/host1x01.o \
hw/host1x02.o \
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 1f93e5e276c0..6c403c8e867b 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -782,7 +782,9 @@ static struct platform_driver tegra_host1x_driver = {
static struct platform_driver * const drivers[] = {
&tegra_host1x_driver,
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
&tegra_mipi_driver,
+#endif
};
static int __init tegra_host1x_init(void)
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index d3855a1c6b47..9be9669d3b1c 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -354,6 +354,8 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
host->debug_op->show_mlocks(host, o);
}
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
extern struct platform_driver tegra_mipi_driver;
+#endif
#endif
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
index e51b43dd15a3..1695e6f3b0d0 100644
--- a/drivers/gpu/host1x/mipi.c
+++ b/drivers/gpu/host1x/mipi.c
@@ -21,195 +21,60 @@
*/
#include <linux/clk.h>
-#include <linux/host1x.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
-#include "dev.h"
+/* only need to support one provider */
+static struct {
+ struct device_node *np;
+ const struct tegra_mipi_ops *ops;
+} provider;
-#define MIPI_CAL_CTRL 0x00
-#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
-#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
-#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
-#define MIPI_CAL_CTRL_START (1 << 0)
-
-#define MIPI_CAL_AUTOCAL_CTRL 0x01
-
-#define MIPI_CAL_STATUS 0x02
-#define MIPI_CAL_STATUS_DONE (1 << 16)
-#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
-
-#define MIPI_CAL_CONFIG_CSIA 0x05
-#define MIPI_CAL_CONFIG_CSIB 0x06
-#define MIPI_CAL_CONFIG_CSIC 0x07
-#define MIPI_CAL_CONFIG_CSID 0x08
-#define MIPI_CAL_CONFIG_CSIE 0x09
-#define MIPI_CAL_CONFIG_CSIF 0x0a
-#define MIPI_CAL_CONFIG_DSIA 0x0e
-#define MIPI_CAL_CONFIG_DSIB 0x0f
-#define MIPI_CAL_CONFIG_DSIC 0x10
-#define MIPI_CAL_CONFIG_DSID 0x11
-
-#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
-#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
-#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
-#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
-#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
-#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
-#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
-
-/* for data and clock lanes */
-#define MIPI_CAL_CONFIG_SELECT (1 << 21)
-
-/* for data lanes */
-#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
-#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
-
-/* for clock lanes */
-#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG0 0x16
-#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
-#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG1 0x17
-#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
-
-#define MIPI_CAL_BIAS_PAD_CFG2 0x18
-#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
-#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
-
-struct tegra_mipi_pad {
- unsigned long data;
- unsigned long clk;
-};
-
-struct tegra_mipi_soc {
- bool has_clk_lane;
- const struct tegra_mipi_pad *pads;
- unsigned int num_pads;
-
- bool clock_enable_override;
- bool needs_vclamp_ref;
-
- /* bias pad configuration settings */
- u8 pad_drive_down_ref;
- u8 pad_drive_up_ref;
-
- u8 pad_vclamp_level;
- u8 pad_vauxp_level;
-
- /* calibration settings for data lanes */
- u8 hspdos;
- u8 hspuos;
- u8 termos;
-
- /* calibration settings for clock lanes */
- u8 hsclkpdos;
- u8 hsclkpuos;
-};
-
-struct tegra_mipi {
- const struct tegra_mipi_soc *soc;
- struct device *dev;
- void __iomem *regs;
- struct mutex lock;
- struct clk *clk;
-
- unsigned long usage_count;
-};
-
-struct tegra_mipi_device {
- struct platform_device *pdev;
- struct tegra_mipi *mipi;
- struct device *device;
- unsigned long pads;
-};
-
-static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
- unsigned long offset)
+int tegra_mipi_enable(struct tegra_mipi_device *device)
{
- return readl(mipi->regs + (offset << 2));
-}
+ if (device->ops->enable)
+ return device->ops->enable(device);
-static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
- unsigned long offset)
-{
- writel(value, mipi->regs + (offset << 2));
+ return 0;
}
+EXPORT_SYMBOL(tegra_mipi_enable);
-static int tegra_mipi_power_up(struct tegra_mipi *mipi)
+int tegra_mipi_disable(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
-
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
- value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
-
- if (mipi->soc->needs_vclamp_ref)
- value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
-
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
-
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- clk_disable(mipi->clk);
+ if (device->ops->disable)
+ return device->ops->disable(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_disable);
-static int tegra_mipi_power_down(struct tegra_mipi *mipi)
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
+ if (device->ops->start_calibration)
+ return device->ops->start_calibration(device);
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- /*
- * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
- * supplies the DSI pads. This must be kept enabled until none of the
- * DSI lanes are used anymore.
- */
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value |= MIPI_CAL_BIAS_PAD_PDVREG;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- /*
- * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
- * control a regulator that supplies current to the pre-driver logic.
- * Powering down this regulator causes DSI to fail, so it must remain
- * powered on until none of the DSI lanes are used anymore.
- */
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
-
- if (mipi->soc->needs_vclamp_ref)
- value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_start_calibration);
- value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
+{
+ if (device->ops->finish_calibration)
+ return device->ops->finish_calibration(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_finish_calibration);
struct tegra_mipi_device *tegra_mipi_request(struct device *device,
struct device_node *np)
{
- struct tegra_mipi_device *dev;
+ struct tegra_mipi_device *mipidev;
struct of_phandle_args args;
int err;
@@ -219,321 +84,51 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
if (err < 0)
return ERR_PTR(err);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
+ if (provider.np != args.np)
+ return ERR_PTR(-ENODEV);
+
+ mipidev = kzalloc(sizeof(*mipidev), GFP_KERNEL);
+ if (!mipidev) {
err = -ENOMEM;
goto out;
}
- dev->pdev = of_find_device_by_node(args.np);
- if (!dev->pdev) {
+ mipidev->pdev = of_find_device_by_node(args.np);
+ if (!mipidev->pdev) {
err = -ENODEV;
goto free;
}
- dev->mipi = platform_get_drvdata(dev->pdev);
- if (!dev->mipi) {
- err = -EPROBE_DEFER;
- goto put;
- }
-
of_node_put(args.np);
- dev->pads = args.args[0];
- dev->device = device;
+ mipidev->ops = provider.ops;
+ mipidev->pads = args.args[0];
- return dev;
+ return mipidev;
-put:
- platform_device_put(dev->pdev);
free:
- kfree(dev);
+ kfree(mipidev);
out:
of_node_put(args.np);
return ERR_PTR(err);
}
EXPORT_SYMBOL(tegra_mipi_request);
-void tegra_mipi_free(struct tegra_mipi_device *device)
+void tegra_mipi_free(struct tegra_mipi_device *mipidev)
{
- platform_device_put(device->pdev);
- kfree(device);
+ platform_device_put(mipidev->pdev);
+ kfree(mipidev);
}
EXPORT_SYMBOL(tegra_mipi_free);
-int tegra_mipi_enable(struct tegra_mipi_device *dev)
-{
- int err = 0;
-
- mutex_lock(&dev->mipi->lock);
-
- if (dev->mipi->usage_count++ == 0)
- err = tegra_mipi_power_up(dev->mipi);
-
- mutex_unlock(&dev->mipi->lock);
-
- return err;
-
-}
-EXPORT_SYMBOL(tegra_mipi_enable);
-
-int tegra_mipi_disable(struct tegra_mipi_device *dev)
+int tegra_mipi_add_provider(struct device_node *np, const struct tegra_mipi_ops *ops)
{
- int err = 0;
+ if (provider.np)
+ return -EBUSY;
- mutex_lock(&dev->mipi->lock);
-
- if (--dev->mipi->usage_count == 0)
- err = tegra_mipi_power_down(dev->mipi);
-
- mutex_unlock(&dev->mipi->lock);
-
- return err;
-
-}
-EXPORT_SYMBOL(tegra_mipi_disable);
-
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
-{
- struct tegra_mipi *mipi = device->mipi;
- void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
- u32 value;
- int err;
-
- err = readl_relaxed_poll_timeout(status_reg, value,
- !(value & MIPI_CAL_STATUS_ACTIVE) &&
- (value & MIPI_CAL_STATUS_DONE), 50,
- 250000);
- mutex_unlock(&device->mipi->lock);
- clk_disable(device->mipi->clk);
-
- return err;
-}
-EXPORT_SYMBOL(tegra_mipi_finish_calibration);
-
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
-{
- const struct tegra_mipi_soc *soc = device->mipi->soc;
- unsigned int i;
- u32 value;
- int err;
-
- err = clk_enable(device->mipi->clk);
- if (err < 0)
- return err;
-
- mutex_lock(&device->mipi->lock);
-
- value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
- MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
- value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
- value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
- value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- for (i = 0; i < soc->num_pads; i++) {
- u32 clk = 0, data = 0;
-
- if (device->pads & BIT(i)) {
- data = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
- MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
- MIPI_CAL_CONFIG_TERMOS(soc->termos);
- clk = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
- MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
- }
-
- tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
-
- if (soc->has_clk_lane && soc->pads[i].clk != 0)
- tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
- }
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
- value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
- value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
- value |= MIPI_CAL_CTRL_PRESCALE(0x2);
-
- if (!soc->clock_enable_override)
- value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
- else
- value |= MIPI_CAL_CTRL_CLKEN_OVR;
-
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /* clear any pending status bits */
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value |= MIPI_CAL_CTRL_START;
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /*
- * Wait for min 72uS to let calibration logic finish calibration
- * sequence codes before waiting for pads idle state to apply the
- * results.
- */
- usleep_range(75, 80);
-
- return 0;
-}
-EXPORT_SYMBOL(tegra_mipi_start_calibration);
-
-static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA },
- { .data = MIPI_CAL_CONFIG_CSIB },
- { .data = MIPI_CAL_CONFIG_CSIC },
- { .data = MIPI_CAL_CONFIG_CSID },
- { .data = MIPI_CAL_CONFIG_CSIE },
- { .data = MIPI_CAL_CONFIG_DSIA },
- { .data = MIPI_CAL_CONFIG_DSIB },
- { .data = MIPI_CAL_CONFIG_DSIC },
- { .data = MIPI_CAL_CONFIG_DSID },
-};
-
-static const struct tegra_mipi_soc tegra114_mipi_soc = {
- .has_clk_lane = false,
- .pads = tegra114_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x4,
- .termos = 0x5,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x4,
-};
-
-static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
-};
-
-static const struct tegra_mipi_soc tegra124_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x1,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_soc tegra132_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = false,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x3,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
- { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
- { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
-};
-
-static const struct tegra_mipi_soc tegra210_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra210_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x1,
- .pad_vauxp_level = 0x1,
- .hspdos = 0x0,
- .hspuos = 0x2,
- .termos = 0x0,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x2,
-};
-
-static const struct of_device_id tegra_mipi_of_match[] = {
- { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
- { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
- { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
- { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
- { },
-};
-
-static int tegra_mipi_probe(struct platform_device *pdev)
-{
- const struct of_device_id *match;
- struct tegra_mipi *mipi;
-
- match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
- if (!match)
- return -ENODEV;
-
- mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
- if (!mipi)
- return -ENOMEM;
-
- mipi->soc = match->data;
- mipi->dev = &pdev->dev;
-
- mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(mipi->regs))
- return PTR_ERR(mipi->regs);
-
- mutex_init(&mipi->lock);
-
- mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
- if (IS_ERR(mipi->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- return PTR_ERR(mipi->clk);
- }
-
- platform_set_drvdata(pdev, mipi);
+ provider.np = np;
+ provider.ops = ops;
return 0;
}
-
-struct platform_driver tegra_mipi_driver = {
- .driver = {
- .name = "tegra-mipi",
- .of_match_table = tegra_mipi_of_match,
- },
- .probe = tegra_mipi_probe,
-};
+EXPORT_SYMBOL(tegra_mipi_add_provider);
diff --git a/drivers/gpu/host1x/tegra114-mipi.c b/drivers/gpu/host1x/tegra114-mipi.c
new file mode 100644
index 000000000000..158a0491f830
--- /dev/null
+++ b/drivers/gpu/host1x/tegra114-mipi.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
+
+#include "dev.h"
+
+#define MIPI_CAL_CTRL 0x00
+#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
+#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
+#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
+#define MIPI_CAL_CTRL_START (1 << 0)
+
+#define MIPI_CAL_AUTOCAL_CTRL 0x01
+
+#define MIPI_CAL_STATUS 0x02
+#define MIPI_CAL_STATUS_DONE (1 << 16)
+#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
+
+#define MIPI_CAL_CONFIG_CSIA 0x05
+#define MIPI_CAL_CONFIG_CSIB 0x06
+#define MIPI_CAL_CONFIG_CSIC 0x07
+#define MIPI_CAL_CONFIG_CSID 0x08
+#define MIPI_CAL_CONFIG_CSIE 0x09
+#define MIPI_CAL_CONFIG_CSIF 0x0a
+#define MIPI_CAL_CONFIG_DSIA 0x0e
+#define MIPI_CAL_CONFIG_DSIB 0x0f
+#define MIPI_CAL_CONFIG_DSIC 0x10
+#define MIPI_CAL_CONFIG_DSID 0x11
+
+#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
+#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
+#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
+#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
+#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
+#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
+#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
+
+/* for data and clock lanes */
+#define MIPI_CAL_CONFIG_SELECT (1 << 21)
+
+/* for data lanes */
+#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
+
+/* for clock lanes */
+#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG0 0x16
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG1 0x17
+#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
+
+#define MIPI_CAL_BIAS_PAD_CFG2 0x18
+#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
+#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
+
+struct tegra_mipi_pad {
+ unsigned long data;
+ unsigned long clk;
+};
+
+struct tegra_mipi_soc {
+ bool has_clk_lane;
+ const struct tegra_mipi_pad *pads;
+ unsigned int num_pads;
+
+ bool clock_enable_override;
+ bool needs_vclamp_ref;
+
+ /* bias pad configuration settings */
+ u8 pad_drive_down_ref;
+ u8 pad_drive_up_ref;
+
+ u8 pad_vclamp_level;
+ u8 pad_vauxp_level;
+
+ /* calibration settings for data lanes */
+ u8 hspdos;
+ u8 hspuos;
+ u8 termos;
+
+ /* calibration settings for clock lanes */
+ u8 hsclkpdos;
+ u8 hsclkpuos;
+};
+
+struct tegra_mipi {
+ const struct tegra_mipi_soc *soc;
+ struct device *dev;
+ void __iomem *regs;
+ struct mutex lock;
+ struct clk *clk;
+
+ unsigned long usage_count;
+};
+
+static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
+ unsigned long offset)
+{
+ return readl(mipi->regs + (offset << 2));
+}
+
+static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
+ unsigned long offset)
+{
+ writel(value, mipi->regs + (offset << 2));
+}
+
+static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+
+ if (mipi->soc->needs_vclamp_ref)
+ value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ clk_disable(mipi->clk);
+
+ return 0;
+}
+
+static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ /*
+ * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
+ * supplies the DSI pads. This must be kept enabled until none of the
+ * DSI lanes are used anymore.
+ */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value |= MIPI_CAL_BIAS_PAD_PDVREG;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ /*
+ * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
+ * control a regulator that supplies current to the pre-driver logic.
+ * Powering down this regulator causes DSI to fail, so it must remain
+ * powered on until none of the DSI lanes are used anymore.
+ */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+
+ if (mipi->soc->needs_vclamp_ref)
+ value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+
+ value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ return 0;
+}
+
+static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (mipi->usage_count++ == 0)
+ err = tegra114_mipi_power_up(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (--mipi->usage_count == 0)
+ err = tegra114_mipi_power_down(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
+ u32 value;
+ int err;
+
+ err = readl_relaxed_poll_timeout(status_reg, value,
+ !(value & MIPI_CAL_STATUS_ACTIVE) &&
+ (value & MIPI_CAL_STATUS_DONE), 50,
+ 250000);
+ mutex_unlock(&mipi->lock);
+ clk_disable(mipi->clk);
+
+ return err;
+}
+
+static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ const struct tegra_mipi_soc *soc = mipi->soc;
+ unsigned int i;
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&mipi->lock);
+
+ value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
+ MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
+ value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
+ value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
+ value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ for (i = 0; i < soc->num_pads; i++) {
+ u32 clk = 0, data = 0;
+
+ if (mipidev->pads & BIT(i)) {
+ data = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
+ MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
+ MIPI_CAL_CONFIG_TERMOS(soc->termos);
+ clk = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
+ MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
+ }
+
+ tegra_mipi_writel(mipi, data, soc->pads[i].data);
+
+ if (soc->has_clk_lane && soc->pads[i].clk != 0)
+ tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
+ }
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
+ value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
+ value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
+ value |= MIPI_CAL_CTRL_PRESCALE(0x2);
+
+ if (!soc->clock_enable_override)
+ value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
+ else
+ value |= MIPI_CAL_CTRL_CLKEN_OVR;
+
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /* clear any pending status bits */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value |= MIPI_CAL_CTRL_START;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /*
+ * Wait for min 72uS to let calibration logic finish calibration
+ * sequence codes before waiting for pads idle state to apply the
+ * results.
+ */
+ usleep_range(75, 80);
+
+ return 0;
+}
+
+static const struct tegra_mipi_ops tegra114_mipi_ops = {
+ .enable = tegra114_mipi_enable,
+ .disable = tegra114_mipi_disable,
+ .start_calibration = tegra114_mipi_start_calibration,
+ .finish_calibration = tegra114_mipi_finish_calibration,
+};
+
+static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA },
+ { .data = MIPI_CAL_CONFIG_CSIB },
+ { .data = MIPI_CAL_CONFIG_CSIC },
+ { .data = MIPI_CAL_CONFIG_CSID },
+ { .data = MIPI_CAL_CONFIG_CSIE },
+ { .data = MIPI_CAL_CONFIG_DSIA },
+ { .data = MIPI_CAL_CONFIG_DSIB },
+ { .data = MIPI_CAL_CONFIG_DSIC },
+ { .data = MIPI_CAL_CONFIG_DSID },
+};
+
+static const struct tegra_mipi_soc tegra114_mipi_soc = {
+ .has_clk_lane = false,
+ .pads = tegra114_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x4,
+ .termos = 0x5,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x4,
+};
+
+static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+};
+
+static const struct tegra_mipi_soc tegra124_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x1,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_soc tegra132_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = false,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x3,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
+ { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
+};
+
+static const struct tegra_mipi_soc tegra210_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra210_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x1,
+ .pad_vauxp_level = 0x1,
+ .hspdos = 0x0,
+ .hspuos = 0x2,
+ .termos = 0x0,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x2,
+};
+
+static const struct of_device_id tegra_mipi_of_match[] = {
+ { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
+ { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
+ { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
+ { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
+ { },
+};
+
+static int tegra_mipi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_mipi *mipi;
+
+ match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+
+ mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
+ if (!mipi)
+ return -ENOMEM;
+
+ mipi->soc = match->data;
+ mipi->dev = &pdev->dev;
+
+ mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(mipi->regs))
+ return PTR_ERR(mipi->regs);
+
+ mutex_init(&mipi->lock);
+
+ mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(mipi->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(mipi->clk);
+ }
+
+ platform_set_drvdata(pdev, mipi);
+
+ return tegra_mipi_add_provider(pdev->dev.of_node,
+ &tegra114_mipi_ops);
+}
+
+struct platform_driver tegra_mipi_driver = {
+ .driver = {
+ .name = "tegra-mipi",
+ .of_match_table = tegra_mipi_of_match,
+ },
+ .probe = tegra_mipi_probe,
+};
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 74c92db1032f..9e3bd6109781 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -12,6 +12,7 @@
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/tegra-mipi-cal.h>
#include <media/v4l2-fwnode.h>
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 9fa9c30a34e6..b1c6514859d3 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -453,16 +453,6 @@ void host1x_client_unregister(struct host1x_client *client);
int host1x_client_suspend(struct host1x_client *client);
int host1x_client_resume(struct host1x_client *client);
-struct tegra_mipi_device;
-
-struct tegra_mipi_device *tegra_mipi_request(struct device *device,
- struct device_node *np);
-void tegra_mipi_free(struct tegra_mipi_device *device);
-int tegra_mipi_enable(struct tegra_mipi_device *device);
-int tegra_mipi_disable(struct tegra_mipi_device *device);
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
-
/* host1x memory contexts */
struct host1x_memory_context {
diff --git a/include/linux/tegra-mipi-cal.h b/include/linux/tegra-mipi-cal.h
new file mode 100644
index 000000000000..77342ee7674f
--- /dev/null
+++ b/include/linux/tegra-mipi-cal.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TEGRA_MIPI_CAL_H_
+#define __TEGRA_MIPI_CAL_H_
+
+struct tegra_mipi_device {
+ const struct tegra_mipi_ops *ops;
+ struct platform_device *pdev;
+ unsigned long pads;
+};
+
+/**
+ * Operations for Tegra MIPI calibration device
+ */
+struct tegra_mipi_ops {
+ /**
+ * @enable:
+ *
+ * Enable MIPI calibration device
+ */
+ int (*enable)(struct tegra_mipi_device *device);
+
+ /**
+ * @disable:
+ *
+ * Disable MIPI calibration device
+ */
+ int (*disable)(struct tegra_mipi_device *device);
+
+ /**
+ * @start_calibration:
+ *
+ * Start MIPI calibration
+ */
+ int (*start_calibration)(struct tegra_mipi_device *device);
+
+ /**
+ * @finish_calibration:
+ *
+ * Finish MIPI calibration
+ */
+ int (*finish_calibration)(struct tegra_mipi_device *device);
+};
+
+int tegra_mipi_add_provider(struct device_node *np, const struct tegra_mipi_ops *ops);
+
+struct tegra_mipi_device *tegra_mipi_request(struct device *device,
+ struct device_node *np);
+void tegra_mipi_free(struct tegra_mipi_device *device);
+
+int tegra_mipi_enable(struct tegra_mipi_device *device);
+int tegra_mipi_disable(struct tegra_mipi_device *device);
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
+
+#endif /* __TEGRA_MIPI_CAL_H_ */
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 10/22] staging: media: tegra-video: vi: improve logic of source requesting
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (8 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 11/22] staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to CSI Svyatoslav Ryhel
` (11 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
By default tegra_channel_get_remote_csi_subdev returns next device in pipe
assuming it is CSI but in case of Tegra20 and Tegra30 it can also be VIP
or even HOST.
Define tegra_channel_get_remote_csi_subdev within CSI and add check if
returned device is actually CSI by comparing subdevice operations.
Previous tegra_channel_get_remote_csi_subdev definition in VI rename to
tegra_channel_get_remote_bridge_subdev and use it only in VI driver since
core VI driver does not care about source and does not call any specific
functions.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/csi.c | 16 ++++++++++++++++
drivers/staging/media/tegra-video/vi.c | 14 +++++++-------
2 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 9e3bd6109781..ef5f054b6d49 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -445,6 +445,22 @@ static const struct v4l2_subdev_ops tegra_csi_ops = {
.pad = &tegra_csi_pad_ops,
};
+struct v4l2_subdev *tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan)
+{
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+
+ pad = media_pad_remote_pad_first(&chan->pad);
+ if (!pad)
+ return NULL;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+ if (!subdev)
+ return NULL;
+
+ return subdev->ops == &tegra_csi_ops ? subdev : NULL;
+}
+
static int tegra_csi_channel_alloc(struct tegra_csi *csi,
struct device_node *node,
unsigned int port_num, unsigned int lanes,
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 90473729b546..04b538e8b514 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -160,8 +160,8 @@ static void tegra_channel_buffer_queue(struct vb2_buffer *vb)
wake_up_interruptible(&chan->start_wait);
}
-struct v4l2_subdev *
-tegra_channel_get_remote_csi_subdev(struct tegra_vi_channel *chan)
+static struct v4l2_subdev *
+tegra_channel_get_remote_bridge_subdev(struct tegra_vi_channel *chan)
{
struct media_pad *pad;
@@ -182,7 +182,7 @@ tegra_channel_get_remote_source_subdev(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
struct media_entity *entity;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
if (!subdev)
return NULL;
@@ -204,7 +204,7 @@ static int tegra_channel_enable_stream(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
int ret;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
ret = v4l2_subdev_call(subdev, video, s_stream, true);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
@@ -217,7 +217,7 @@ static int tegra_channel_disable_stream(struct tegra_vi_channel *chan)
struct v4l2_subdev *subdev;
int ret;
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
ret = v4l2_subdev_call(subdev, video, s_stream, false);
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
@@ -1630,11 +1630,11 @@ static int tegra_vi_graph_notify_complete(struct v4l2_async_notifier *notifier)
goto unregister_video;
}
- subdev = tegra_channel_get_remote_csi_subdev(chan);
+ subdev = tegra_channel_get_remote_bridge_subdev(chan);
if (!subdev) {
ret = -ENODEV;
dev_err(vi->dev,
- "failed to get remote csi subdev: %d\n", ret);
+ "failed to get remote bridge subdev: %d\n", ret);
goto unregister_video;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 11/22] staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to CSI
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (9 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 10/22] staging: media: tegra-video: vi: improve logic of source requesting Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node Svyatoslav Ryhel
` (10 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
The avdd-dsi-csi-supply is CSI power supply not VI, hence move it to
proper place.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Tested-by: Luca Ceresoli <luca.ceresoli@bootlin.com> # Tegra20 VIP
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/csi.c | 19 ++++++++++++++++++-
drivers/staging/media/tegra-video/csi.h | 2 ++
drivers/staging/media/tegra-video/vi.c | 23 ++---------------------
drivers/staging/media/tegra-video/vi.h | 2 --
4 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index ef5f054b6d49..7d70478a07aa 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -710,6 +710,8 @@ static int __maybe_unused csi_runtime_suspend(struct device *dev)
clk_bulk_disable_unprepare(csi->soc->num_clks, csi->clks);
+ regulator_disable(csi->vdd);
+
return 0;
}
@@ -718,13 +720,23 @@ static int __maybe_unused csi_runtime_resume(struct device *dev)
struct tegra_csi *csi = dev_get_drvdata(dev);
int ret;
+ ret = regulator_enable(csi->vdd);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD supply: %d\n", ret);
+ return ret;
+ }
+
ret = clk_bulk_prepare_enable(csi->soc->num_clks, csi->clks);
if (ret < 0) {
dev_err(csi->dev, "failed to enable clocks: %d\n", ret);
- return ret;
+ goto disable_vdd;
}
return 0;
+
+disable_vdd:
+ regulator_disable(csi->vdd);
+ return ret;
}
static int tegra_csi_init(struct host1x_client *client)
@@ -802,6 +814,11 @@ static int tegra_csi_probe(struct platform_device *pdev)
return ret;
}
+ csi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
+ if (IS_ERR(csi->vdd))
+ return dev_err_probe(&pdev->dev, PTR_ERR(csi->vdd),
+ "failed to get VDD supply");
+
if (!pdev->dev.pm_domain) {
ret = -ENOENT;
dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret);
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 3ed2dbc73ce9..1550defb115a 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -137,6 +137,7 @@ struct tegra_csi_soc {
* @client: host1x_client struct
* @iomem: register base
* @clks: clock for CSI and CIL
+ * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
* @soc: pointer to SoC data structure
* @ops: csi operations
* @csi_chans: list head for CSI channels
@@ -146,6 +147,7 @@ struct tegra_csi {
struct host1x_client client;
void __iomem *iomem;
struct clk_bulk_data *clks;
+ struct regulator *vdd;
const struct tegra_csi_soc *soc;
const struct tegra_csi_ops *ops;
struct list_head csi_chans;
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 04b538e8b514..70607a3eeee1 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1417,29 +1417,19 @@ static int __maybe_unused vi_runtime_resume(struct device *dev)
struct tegra_vi *vi = dev_get_drvdata(dev);
int ret;
- ret = regulator_enable(vi->vdd);
- if (ret) {
- dev_err(dev, "failed to enable VDD supply: %d\n", ret);
- return ret;
- }
-
ret = clk_set_rate(vi->clk, vi->soc->vi_max_clk_hz);
if (ret) {
dev_err(dev, "failed to set vi clock rate: %d\n", ret);
- goto disable_vdd;
+ return ret;
}
ret = clk_prepare_enable(vi->clk);
if (ret) {
dev_err(dev, "failed to enable vi clock: %d\n", ret);
- goto disable_vdd;
+ return ret;
}
return 0;
-
-disable_vdd:
- regulator_disable(vi->vdd);
- return ret;
}
static int __maybe_unused vi_runtime_suspend(struct device *dev)
@@ -1448,8 +1438,6 @@ static int __maybe_unused vi_runtime_suspend(struct device *dev)
clk_disable_unprepare(vi->clk);
- regulator_disable(vi->vdd);
-
return 0;
}
@@ -1894,13 +1882,6 @@ static int tegra_vi_probe(struct platform_device *pdev)
return ret;
}
- vi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
- if (IS_ERR(vi->vdd)) {
- ret = PTR_ERR(vi->vdd);
- dev_err(&pdev->dev, "failed to get VDD supply: %d\n", ret);
- return ret;
- }
-
if (!pdev->dev.pm_domain) {
ret = -ENOENT;
dev_warn(&pdev->dev, "PM domain is not attached: %d\n", ret);
diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
index cac0c0d0e225..bfadde8858d4 100644
--- a/drivers/staging/media/tegra-video/vi.h
+++ b/drivers/staging/media/tegra-video/vi.h
@@ -94,7 +94,6 @@ struct tegra_vi_soc {
* @client: host1x_client struct
* @iomem: register base
* @clk: main clock for VI block
- * @vdd: vdd regulator for VI hardware, normally it is avdd_dsi_csi
* @soc: pointer to SoC data structure
* @ops: vi operations
* @vi_chans: list head for VI channels
@@ -104,7 +103,6 @@ struct tegra_vi {
struct host1x_client client;
void __iomem *iomem;
struct clk *clk;
- struct regulator *vdd;
const struct tegra_vi_soc *soc;
const struct tegra_vi_ops *ops;
struct list_head vi_chans;
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (10 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 11/22] staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to CSI Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-01 4:27 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 13/22] staging: media: tegra-video: tegra20: set correct maximum width and height Svyatoslav Ryhel
` (9 subsequent siblings)
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
avdd-dsi-csi-supply belongs in CSI node, not VI.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 4 ++--
arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
index 584461f3a619..4a64fe510f03 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
@@ -20,10 +20,10 @@ dpaux@54040000 {
vi@54080000 {
status = "okay";
- avdd-dsi-csi-supply = <&vdd_dsi_csi>;
-
csi@838 {
status = "okay";
+
+ avdd-dsi-csi-supply = <&vdd_dsi_csi>;
};
};
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
index ec0e84cb83ef..f1d2606d9808 100644
--- a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
+++ b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
@@ -64,10 +64,10 @@ dpaux@54040000 {
vi@54080000 {
status = "okay";
- avdd-dsi-csi-supply = <&vdd_sys_1v2>;
-
csi@838 {
status = "okay";
+
+ avdd-dsi-csi-supply = <&vdd_sys_1v2>;
};
};
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 13/22] staging: media: tegra-video: tegra20: set correct maximum width and height
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (11 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 14/22] staging: media: tegra-video: tegra20: add support for second output of VI Svyatoslav Ryhel
` (8 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Maximum width and height for Tegra20 and Tegra30 is determined by
respective register field, rounded down to factor of 2, which is 8191U
rounded down to 8190U.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/tegra20.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 7b8f8f810b35..3e2d746638b6 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -23,11 +23,10 @@
#define TEGRA_VI_SYNCPT_WAIT_TIMEOUT msecs_to_jiffies(200)
-/* This are just good-sense numbers. The actual min/max is not documented. */
#define TEGRA20_MIN_WIDTH 32U
+#define TEGRA20_MAX_WIDTH 8190U
#define TEGRA20_MIN_HEIGHT 32U
-#define TEGRA20_MAX_WIDTH 2048U
-#define TEGRA20_MAX_HEIGHT 2048U
+#define TEGRA20_MAX_HEIGHT 8190U
/* --------------------------------------------------------------------------
* Registers
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 14/22] staging: media: tegra-video: tegra20: add support for second output of VI
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (12 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 13/22] staging: media: tegra-video: tegra20: set correct maximum width and height Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations Svyatoslav Ryhel
` (7 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
VI in Tegra20/Tegra30 has 2 VI outputs with different set of supported
formats. Convert output registers to macros for simpler work with both
outputs since apart formats their layout matches.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/tegra20.c | 83 ++++++++++++---------
1 file changed, 47 insertions(+), 36 deletions(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 3e2d746638b6..7c3ff843235d 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -28,13 +28,19 @@
#define TEGRA20_MIN_HEIGHT 32U
#define TEGRA20_MAX_HEIGHT 8190U
+/* Tegra20/Tegra30 has 2 outputs in VI */
+enum tegra_vi_out {
+ TEGRA_VI_OUT_1 = 0,
+ TEGRA_VI_OUT_2 = 1,
+};
+
/* --------------------------------------------------------------------------
* Registers
*/
-#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060
-#define VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT BIT(8)
-#define VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT 0
+#define TEGRA_VI_CONT_SYNCPT_OUT(n) (0x0060 + (n) * 4)
+#define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
+#define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
#define TEGRA_VI_VI_INPUT_CONTROL 0x0088
#define VI_INPUT_FIELD_DETECT BIT(27)
@@ -46,6 +52,7 @@
#define VI_INPUT_YUV_INPUT_FORMAT_YVYU (3 << VI_INPUT_YUV_INPUT_FORMAT_SFT)
#define VI_INPUT_INPUT_FORMAT_SFT 2 /* bits [5:2] */
#define VI_INPUT_INPUT_FORMAT_YUV422 (0 << VI_INPUT_INPUT_FORMAT_SFT)
+#define VI_INPUT_INPUT_FORMAT_BAYER (2 << VI_INPUT_INPUT_FORMAT_SFT)
#define VI_INPUT_VIP_INPUT_ENABLE BIT(1)
#define TEGRA_VI_VI_CORE_CONTROL 0x008c
@@ -66,7 +73,7 @@
#define VI_VI_CORE_CONTROL_OUTPUT_TO_EPP_SFT 2
#define VI_VI_CORE_CONTROL_OUTPUT_TO_ISP_SFT 0
-#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090
+#define TEGRA_VI_VI_OUTPUT_CONTROL(n) (0x0090 + (n) * 4)
#define VI_OUTPUT_FORMAT_EXT BIT(22)
#define VI_OUTPUT_V_DIRECTION BIT(20)
#define VI_OUTPUT_H_DIRECTION BIT(19)
@@ -80,6 +87,8 @@
#define VI_OUTPUT_OUTPUT_FORMAT_SFT 0
#define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
+/* TEGRA_VI_OUT_2 supported formats */
+#define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define TEGRA_VI_VIP_H_ACTIVE 0x00a4
#define VI_VIP_H_ACTIVE_PERIOD_SFT 16 /* active pixels/line, must be even */
@@ -89,26 +98,26 @@
#define VI_VIP_V_ACTIVE_PERIOD_SFT 16 /* active lines */
#define VI_VIP_V_ACTIVE_START_SFT 0
-#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4
-#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8
+#define TEGRA_VI_VB0_START_ADDRESS(n) (0x00c4 + (n) * 44)
+#define TEGRA_VI_VB0_BASE_ADDRESS(n) (0x00c8 + (n) * 44)
#define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc
#define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0
#define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4
#define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8
-#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0
-#define VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT 16
-#define VI_FIRST_OUTPUT_FRAME_WIDTH_SFT 0
+#define TEGRA_VI_OUTPUT_FRAME_SIZE(n) (0x00e0 + (n) * 24)
+#define VI_OUTPUT_FRAME_HEIGHT_SFT 16
+#define VI_OUTPUT_FRAME_WIDTH_SFT 0
-#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4
+#define TEGRA_VI_VB0_COUNT(n) (0x00e4 + (n) * 24)
-#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8
-#define VI_VB0_SIZE_FIRST_V_SFT 16
-#define VI_VB0_SIZE_FIRST_H_SFT 0
+#define TEGRA_VI_VB0_SIZE(n) (0x00e8 + (n) * 24)
+#define VI_VB0_SIZE_V_SFT 16
+#define VI_VB0_SIZE_H_SFT 0
-#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec
-#define VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT 30
-#define VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT 0
+#define TEGRA_VI_VB0_BUFFER_STRIDE(n) (0x00ec + (n) * 24)
+#define VI_VB0_BUFFER_STRIDE_CHROMA_SFT 30
+#define VI_VB0_BUFFER_STRIDE_LUMA_SFT 0
#define TEGRA_VI_H_LPF_CONTROL 0x0108
#define VI_H_LPF_CONTROL_CHROMA_SFT 16
@@ -136,7 +145,7 @@
#define VI_CAMERA_CONTROL_TEST_MODE BIT(1)
#define VI_CAMERA_CONTROL_VIP_ENABLE BIT(0)
-#define TEGRA_VI_VI_ENABLE 0x01a4
+#define TEGRA_VI_VI_ENABLE(n) (0x01a4 + (n) * 4)
#define VI_VI_ENABLE_SW_FLOW_CONTROL_OUT1 BIT(1)
#define VI_VI_ENABLE_FIRST_OUTPUT_TO_MEM_DISABLE BIT(0)
@@ -366,8 +375,8 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
- tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, base);
- tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS_FIRST, base + chan->start_offset);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_1), base);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), base + chan->start_offset);
break;
}
}
@@ -455,6 +464,7 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
int stride_l = chan->format.bytesperline;
int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 ||
output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0;
+ enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
int main_output_format;
int yuv_output_format;
@@ -472,33 +482,33 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
/* Set up raise-on-edge, so we get an interrupt on end of frame. */
tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
- tegra20_vi_write(chan, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL,
+ tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
(chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
(chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
/* Set up frame size */
- tegra20_vi_write(chan, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE,
- height << VI_FIRST_OUTPUT_FRAME_HEIGHT_SFT |
- width << VI_FIRST_OUTPUT_FRAME_WIDTH_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
+ height << VI_OUTPUT_FRAME_HEIGHT_SFT |
+ width << VI_OUTPUT_FRAME_WIDTH_SFT);
/* First output memory enabled */
- tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0);
+ tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0);
/* Set the number of frames in the buffer */
- tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT_FIRST, 1);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_COUNT(output_channel), 1);
/* Set up buffer frame size */
- tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE_FIRST,
- height << VI_VB0_SIZE_FIRST_V_SFT |
- width << VI_VB0_SIZE_FIRST_H_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_SIZE(output_channel),
+ height << VI_VB0_SIZE_V_SFT |
+ width << VI_VB0_SIZE_H_SFT);
- tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST,
- stride_l << VI_VB0_BUFFER_STRIDE_FIRST_LUMA_SFT |
- stride_c << VI_VB0_BUFFER_STRIDE_FIRST_CHROMA_SFT);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BUFFER_STRIDE(output_channel),
+ stride_l << VI_VB0_BUFFER_STRIDE_LUMA_SFT |
+ stride_c << VI_VB0_BUFFER_STRIDE_CHROMA_SFT);
- tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE, 0);
+ tegra20_vi_write(chan, TEGRA_VI_VI_ENABLE(output_channel), 0);
}
static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
@@ -587,7 +597,7 @@ const struct tegra_vi_soc tegra20_vi_soc = {
.nformats = ARRAY_SIZE(tegra20_video_formats),
.default_video_format = &tegra20_video_formats[0],
.ops = &tegra20_vi_ops,
- .vi_max_channels = 1, /* parallel input (VIP) */
+ .vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */
.vi_max_clk_hz = 150000000,
.has_h_v_flip = true,
};
@@ -607,6 +617,7 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&vip_chan->subdev);
int width = vi_chan->format.width;
int height = vi_chan->format.height;
+ enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
unsigned int main_input_format;
unsigned int yuv_input_format;
@@ -637,10 +648,10 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
GENMASK(9, 2) << VI_DATA_INPUT_SFT);
tegra20_vi_write(vi_chan, TEGRA_VI_PIN_INVERSION, 0);
- tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT_1,
- VI_CONT_SYNCPT_OUT_1_CONTINUOUS_SYNCPT |
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
host1x_syncpt_id(vi_chan->mw_ack_sp[0])
- << VI_CONT_SYNCPT_OUT_1_SYNCPT_IDX_SFT);
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (13 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 14/22] staging: media: tegra-video: tegra20: add support for second output of VI Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-01 4:38 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 16/22] staging: media: tegra-video: tegra20: set VI HW revision Svyatoslav Ryhel
` (6 subsequent siblings)
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Simplify format align calculations by slightly modifying supported formats
structure. Adjusted U and V offset calculations for planar formats since
YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
and V) so stride is width * 3/2, but offset must be calculated with plain
width since each plain has stride width * 1. This aligns with downstream
behavior which uses same approach for offset calculations.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
drivers/staging/media/tegra-video/vi.h | 3 +-
2 files changed, 27 insertions(+), 34 deletions(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 7c3ff843235d..b7a39723dfc2 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- pix->bytesperline = roundup(pix->width, 2) * 2;
- pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
- break;
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- pix->bytesperline = roundup(pix->width, 8);
- pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
- break;
- }
+ pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
+ pix->sizeimage = pix->bytesperline * pix->height;
}
/*
@@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
{
unsigned int stride = chan->format.bytesperline;
unsigned int height = chan->format.height;
+ unsigned int width = chan->format.width;
chan->start_offset = 0;
@@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- chan->addr_offset_u = stride * height;
- chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
+ chan->addr_offset_u = width * height;
+ chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
/* For YVU420, we swap the locations of the U and V planes. */
if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
@@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
chan->start_offset_v = chan->addr_offset_v;
if (chan->vflip) {
- chan->start_offset += stride * (height - 1);
- chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
- chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
+ chan->start_offset += width * (height - 1);
+ chan->start_offset_u += (width / 2) * ((height / 2) - 1);
+ chan->start_offset_v += (width / 2) * ((height / 2) - 1);
}
if (chan->hflip) {
- chan->start_offset += stride - 1;
- chan->start_offset_u += (stride / 2) - 1;
- chan->start_offset_v += (stride / 2) - 1;
+ chan->start_offset += width - 1;
+ chan->start_offset_u += (width / 2) - 1;
+ chan->start_offset_v += (width / 2) - 1;
}
break;
}
@@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
.vi_stop_streaming = tegra20_vi_stop_streaming,
};
-#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
-{ \
- .code = MEDIA_BUS_FMT_##MBUS_CODE, \
- .bpp = BPP, \
- .fourcc = V4L2_PIX_FMT_##FOURCC, \
+#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
+{ \
+ .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
+ .bit_width = BIT_WIDTH, \
+ .code = MEDIA_BUS_FMT_##MBUS_CODE, \
+ .bpp = BPP, \
+ .fourcc = V4L2_PIX_FMT_##FOURCC, \
}
static const struct tegra_video_format tegra20_video_formats[] = {
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
- TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
- TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
- TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
- TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
+ /* YUV422 */
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
};
const struct tegra_vi_soc tegra20_vi_soc = {
diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
index bfadde8858d4..5cbc0606ed6c 100644
--- a/drivers/staging/media/tegra-video/vi.h
+++ b/drivers/staging/media/tegra-video/vi.h
@@ -281,7 +281,8 @@ enum tegra_image_dt {
* @img_dt: MIPI CSI-2 data type (for CSI-2 only)
* @bit_width: format width in bits per component (for CSI/Tegra210 only)
* @code: media bus format code
- * @bpp: bytes per pixel (when stored in memory)
+ * @bpp: bytes per pixel (when stored in memory) for Tegra210,
+ * bits per pixel for Tegra20/Tegra30
* @img_fmt: image format (for CSI/Tegra210 only)
* @fourcc: V4L2 pixel format FCC identifier
*/
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 16/22] staging: media: tegra-video: tegra20: set VI HW revision
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (14 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 17/22] staging: media: tegra-video: tegra20: increase maximum VI clock frequency Svyatoslav Ryhel
` (5 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
According to TRM Tegra20, Tegra30 and Tegra114 have VI revision 1,
Tegra124 has revision 2 and Tegra210 has revision 3. Set correct revision
in tegra20_vi_soc like tegra210 does.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/tegra20.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index b7a39723dfc2..4b69b556387c 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -589,6 +589,7 @@ const struct tegra_vi_soc tegra20_vi_soc = {
.nformats = ARRAY_SIZE(tegra20_video_formats),
.default_video_format = &tegra20_video_formats[0],
.ops = &tegra20_vi_ops,
+ .hw_revision = 1,
.vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */
.vi_max_clk_hz = 150000000,
.has_h_v_flip = true,
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 17/22] staging: media: tegra-video: tegra20: increase maximum VI clock frequency
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (15 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 16/22] staging: media: tegra-video: tegra20: set VI HW revision Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 18/22] staging: media: tegra-video: tegra20: expand format support with RAW8/10 and YUV422 1X16 Svyatoslav Ryhel
` (4 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Increase maximum VI clock frequency to 450MHz to allow correct work with
high resolution camera sensors.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/tegra20.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 4b69b556387c..43f545e6c45b 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -591,7 +591,7 @@ const struct tegra_vi_soc tegra20_vi_soc = {
.ops = &tegra20_vi_ops,
.hw_revision = 1,
.vi_max_channels = 2, /* TEGRA_VI_OUT_1 and TEGRA_VI_OUT_2 */
- .vi_max_clk_hz = 150000000,
+ .vi_max_clk_hz = 450000000,
.has_h_v_flip = true,
};
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 18/22] staging: media: tegra-video: tegra20: expand format support with RAW8/10 and YUV422 1X16
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (16 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 17/22] staging: media: tegra-video: tegra20: increase maximum VI clock frequency Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 19/22] staging: media: tegra-video: tegra20: adjust luma buffer stride Svyatoslav Ryhel
` (3 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Add support for Bayer formats (RAW8 and RAW10) and YUV42x 1X16 versions of
existing YUV42x 2X8.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/tegra20.c | 75 ++++++++++++++++++++-
1 file changed, 72 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 43f545e6c45b..fba90cafb9be 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -187,6 +187,18 @@ static void tegra20_vi_get_input_formats(struct tegra_vi_channel *chan,
case MEDIA_BUS_FMT_YVYU8_2X8:
(*yuv_input_format) = VI_INPUT_YUV_INPUT_FORMAT_YVYU;
break;
+ /* RAW8 */
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ /* RAW10 */
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ (*main_input_format) = VI_INPUT_INPUT_FORMAT_BAYER;
+ break;
}
}
@@ -221,6 +233,18 @@ static void tegra20_vi_get_output_formats(struct tegra_vi_channel *chan,
case V4L2_PIX_FMT_YVU420:
(*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR;
break;
+ /* RAW8 */
+ case V4L2_PIX_FMT_SBGGR8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SRGGB8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ (*main_output_format) = VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT;
+ break;
}
}
@@ -302,6 +326,16 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
+ /* RAW8 */
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SBGGR10:
if (chan->vflip)
chan->start_offset += stride * (height - 1);
if (chan->hflip)
@@ -367,6 +401,19 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_1), base);
tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), base + chan->start_offset);
break;
+ /* RAW8 */
+ case V4L2_PIX_FMT_SRGGB8:
+ case V4L2_PIX_FMT_SGRBG8:
+ case V4L2_PIX_FMT_SGBRG8:
+ case V4L2_PIX_FMT_SBGGR8:
+ /* RAW10 */
+ case V4L2_PIX_FMT_SRGGB10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SBGGR10:
+ tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_2), base);
+ tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_2), base + chan->start_offset);
+ break;
}
}
@@ -448,12 +495,15 @@ static int tegra20_chan_capture_kthread_start(void *data)
static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
{
u32 output_fourcc = chan->format.pixelformat;
+ u32 data_type = chan->fmtinfo->img_dt;
int width = chan->format.width;
int height = chan->format.height;
int stride_l = chan->format.bytesperline;
int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 ||
output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0;
- enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
+ enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
+ data_type == TEGRA_IMAGE_DT_RAW10) ?
+ TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
int main_output_format;
int yuv_output_format;
@@ -580,8 +630,25 @@ static const struct tegra_video_format tegra20_video_formats[] = {
TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 16, UYVY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_1X16, 16, VYUY),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_1X16, 16, YUYV),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_1X16, 16, YVYU),
+ /* YUV420P */
TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 12, YUV420),
+ TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_1X16, 12, YVU420),
+ /* RAW 8 */
+ TEGRA20_VIDEO_FMT(RAW8, 8, SRGGB8_1X8, 16, SRGGB8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SGRBG8_1X8, 16, SGRBG8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SGBRG8_1X8, 16, SGBRG8),
+ TEGRA20_VIDEO_FMT(RAW8, 8, SBGGR8_1X8, 16, SBGGR8),
+ /* RAW 10 */
+ TEGRA20_VIDEO_FMT(RAW10, 10, SRGGB10_1X10, 16, SRGGB10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SGRBG10_1X10, 16, SGRBG10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SGBRG10_1X10, 16, SGBRG10),
+ TEGRA20_VIDEO_FMT(RAW10, 10, SBGGR10_1X10, 16, SBGGR10),
};
const struct tegra_vi_soc tegra20_vi_soc = {
@@ -608,10 +675,12 @@ const struct tegra_vi_soc tegra20_vi_soc = {
static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
{
struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&vip_chan->subdev);
+ u32 data_type = vi_chan->fmtinfo->img_dt;
int width = vi_chan->format.width;
int height = vi_chan->format.height;
- enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
-
+ enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
+ data_type == TEGRA_IMAGE_DT_RAW10) ?
+ TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
unsigned int main_input_format;
unsigned int yuv_input_format;
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 19/22] staging: media: tegra-video: tegra20: adjust luma buffer stride
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (17 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 18/22] staging: media: tegra-video: tegra20: expand format support with RAW8/10 and YUV422 1X16 Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI Svyatoslav Ryhel
` (2 subsequent siblings)
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Luma buffer stride is calculated by multiplying height in pixels of image
by bytes per line. Adjust that value accordingly.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/staging/media/tegra-video/tegra20.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index fba90cafb9be..8c9655ffa886 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -498,7 +498,7 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
u32 data_type = chan->fmtinfo->img_dt;
int width = chan->format.width;
int height = chan->format.height;
- int stride_l = chan->format.bytesperline;
+ int stride_l = chan->format.bytesperline * height;
int stride_c = (output_fourcc == V4L2_PIX_FMT_YUV420 ||
output_fourcc == V4L2_PIX_FMT_YVU420) ? 1 : 0;
enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (18 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 19/22] staging: media: tegra-video: tegra20: adjust luma buffer stride Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-02 1:52 ` Rob Herring
2025-09-25 15:16 ` [PATCH v3 21/22] ARM: tegra: add CSI nodes for Tegra20 and Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 22/22] staging: media: tegra-video: add CSI support " Svyatoslav Ryhel
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Document CSI HW block found in Tegra20 and Tegra30 SoC.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../display/tegra/nvidia,tegra20-csi.yaml | 135 ++++++++++++++++++
1 file changed, 135 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
new file mode 100644
index 000000000000..817b3097846b
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-csi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra20 CSI controller
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+properties:
+ compatible:
+ enum:
+ - nvidia,tegra20-csi
+ - nvidia,tegra30-csi
+
+ reg:
+ maxItems: 1
+
+ clocks: true
+ clock-names: true
+
+ avdd-dsi-csi-supply:
+ description: DSI/CSI power supply. Must supply 1.2 V.
+
+ power-domains:
+ maxItems: 1
+
+ "#nvidia,mipi-calibrate-cells":
+ description:
+ The number of cells in a MIPI calibration specifier. Should be 1.
+ The single cell specifies an id of the pad that need to be
+ calibrated for a given device. Valid pad ids for receiver would be
+ 0 for CSI-A; 1 for CSI-B; 2 for DSI-A and 3 for DSI-B.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ const: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+patternProperties:
+ "^channel@[0-1]$":
+ type: object
+ description: channel 0 represents CSI-A and 1 represents CSI-B
+ additionalProperties: false
+
+ properties:
+ reg:
+ maximum: 1
+
+ nvidia,mipi-calibrate:
+ description: Should contain a phandle and a specifier specifying
+ which pad is used by this CSI channel and needs to be calibrated.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: port receiving the video stream from the sensor
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ required:
+ - data-lanes
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: port sending the video stream to the VI
+
+ required:
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - port@0
+ - port@1
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra20-csi
+ then:
+ properties:
+ clocks:
+ items:
+ - description: module clock
+
+ clock-names: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - nvidia,tegra30-csi
+ then:
+ properties:
+ clocks:
+ items:
+ - description: module clock
+ - description: PAD A clock
+ - description: PAD B clock
+
+ clock-names:
+ items:
+ - const: csi
+ - const: csia-pad
+ - const: csib-pad
+
+additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - "#address-cells"
+ - "#size-cells"
+
+# see nvidia,tegra20-vi.yaml for an example
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 21/22] ARM: tegra: add CSI nodes for Tegra20 and Tegra30
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (19 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 22/22] staging: media: tegra-video: add CSI support " Svyatoslav Ryhel
21 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Add CSI node to Tegra20 and Tegra30 device trees.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
arch/arm/boot/dts/nvidia/tegra20.dtsi | 19 ++++++++++++++++++-
arch/arm/boot/dts/nvidia/tegra30.dtsi | 24 ++++++++++++++++++++++--
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/nvidia/tegra20.dtsi b/arch/arm/boot/dts/nvidia/tegra20.dtsi
index 6ae07b316c8a..5cdbf1246cf8 100644
--- a/arch/arm/boot/dts/nvidia/tegra20.dtsi
+++ b/arch/arm/boot/dts/nvidia/tegra20.dtsi
@@ -64,7 +64,7 @@ mpe@54040000 {
vi@54080000 {
compatible = "nvidia,tegra20-vi";
- reg = <0x54080000 0x00040000>;
+ reg = <0x54080000 0x00000800>;
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_VI>;
resets = <&tegra_car 20>;
@@ -72,6 +72,23 @@ vi@54080000 {
power-domains = <&pd_venc>;
operating-points-v2 = <&vi_dvfs_opp_table>;
status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ ranges = <0x0 0x54080000 0x4000>;
+
+ csi: csi@800 {
+ compatible = "nvidia,tegra20-csi";
+ reg = <0x800 0x200>;
+ clocks = <&tegra_car TEGRA20_CLK_CSI>;
+ power-domains = <&pd_venc>;
+ #nvidia,mipi-calibrate-cells = <1>;
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
};
epp@540c0000 {
diff --git a/arch/arm/boot/dts/nvidia/tegra30.dtsi b/arch/arm/boot/dts/nvidia/tegra30.dtsi
index 20b3248d4d2f..be752a245a55 100644
--- a/arch/arm/boot/dts/nvidia/tegra30.dtsi
+++ b/arch/arm/boot/dts/nvidia/tegra30.dtsi
@@ -150,8 +150,8 @@ mpe@54040000 {
};
vi@54080000 {
- compatible = "nvidia,tegra30-vi";
- reg = <0x54080000 0x00040000>;
+ compatible = "nvidia,tegra30-vi", "nvidia,tegra20-vi";
+ reg = <0x54080000 0x00000800>;
interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA30_CLK_VI>;
resets = <&tegra_car 20>;
@@ -162,6 +162,26 @@ vi@54080000 {
iommus = <&mc TEGRA_SWGROUP_VI>;
status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ ranges = <0x0 0x54080000 0x4000>;
+
+ csi: csi@800 {
+ compatible = "nvidia,tegra30-csi";
+ reg = <0x800 0x200>;
+ clocks = <&tegra_car TEGRA30_CLK_CSI>,
+ <&tegra_car TEGRA30_CLK_CSIA_PAD>,
+ <&tegra_car TEGRA30_CLK_CSIB_PAD>;
+ clock-names = "csi", "csia-pad", "csib-pad";
+ power-domains = <&pd_venc>;
+ #nvidia,mipi-calibrate-cells = <1>;
+ status = "disabled";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
};
epp@540c0000 {
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
` (20 preceding siblings ...)
2025-09-25 15:16 ` [PATCH v3 21/22] ARM: tegra: add CSI nodes for Tegra20 and Tegra30 Svyatoslav Ryhel
@ 2025-09-25 15:16 ` Svyatoslav Ryhel
2025-10-01 5:04 ` Mikko Perttunen
21 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-09-25 15:16 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Svyatoslav Ryhel, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
Add support for MIPI CSI device and calibration logic found in Tegra20 and
Tegra30 SoC.
To get CSI operational, an additional syncpoint was allocated to serve as
the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
frame start events. That said, the frame capture function was refactored
to reflect the addition of the CSI syncpoint, and the CSI-specific
configuration is guarded by the presence of a passed CSI channel structure
pointer.
The camera capture setup's configuration was reconsidered: the first two
writes must be done before tegra_channel_set_stream for MIPI calibration
to work properly; the third write was moved to VIP/CSI-specific functions
since it must be source-specific; the function was placed after
tegra_channel_set_stream so the initial sequence is preserved and expanded.
CSI configuration sequences were added based on downstream 3.1 kernel
sources and adjusted to the existing video-tegra framework. Although
Tegra20 and Tegra30 have the same set of configurations, they differ by
the number of clocks used by CSI.
Dropped the software syncpoint counters in favor of reading syncpoints
directly and passing the incremented value to the polling function. If the
syncpoint increase fails, the PP is reset. This change should prevent
possible race conditions.
MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
have no dedicated hardware block for these operations and use CSI. These
calls are used for both CSI and DSI to work properly, which is why MIPI
calibration cannot be contained within CSI. The pads passed to the
calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
DSI-B (4).
Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/staging/media/tegra-video/csi.c | 19 +
drivers/staging/media/tegra-video/csi.h | 4 +
drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
drivers/staging/media/tegra-video/vi.h | 2 -
drivers/staging/media/tegra-video/video.c | 6 +
5 files changed, 592 insertions(+), 47 deletions(-)
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 7d70478a07aa..92ee4c84a988 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
csi->dev = &pdev->dev;
csi->ops = csi->soc->ops;
+
+ if (csi->soc->mipi_ops)
+ tegra_mipi_add_provider(pdev->dev.of_node,
+ csi->soc->mipi_ops);
+
+ mutex_init(&csi->mipi_lock);
+
platform_set_drvdata(pdev, csi);
pm_runtime_enable(&pdev->dev);
@@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+extern const struct tegra_csi_soc tegra20_csi_soc;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+extern const struct tegra_csi_soc tegra30_csi_soc;
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
extern const struct tegra_csi_soc tegra210_csi_soc;
#endif
static const struct of_device_id tegra_csi_of_id_table[] = {
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
#endif
diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
index 1550defb115a..422f30655945 100644
--- a/drivers/staging/media/tegra-video/csi.h
+++ b/drivers/staging/media/tegra-video/csi.h
@@ -115,6 +115,7 @@ struct tegra_csi_ops {
* struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
*
* @ops: csi hardware operations
+ * @mipi_ops: MIPI calibration operations
* @csi_max_channels: supported max streaming channels
* @clk_names: csi and cil clock names
* @num_clks: total clocks count
@@ -123,6 +124,7 @@ struct tegra_csi_ops {
*/
struct tegra_csi_soc {
const struct tegra_csi_ops *ops;
+ const struct tegra_mipi_ops *mipi_ops;
unsigned int csi_max_channels;
const char * const *clk_names;
unsigned int num_clks;
@@ -140,6 +142,7 @@ struct tegra_csi_soc {
* @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
* @soc: pointer to SoC data structure
* @ops: csi operations
+ * @mipi_lock: for MIPI calibration operations
* @csi_chans: list head for CSI channels
*/
struct tegra_csi {
@@ -150,6 +153,7 @@ struct tegra_csi {
struct regulator *vdd;
const struct tegra_csi_soc *soc;
const struct tegra_csi_ops *ops;
+ struct mutex mipi_lock;
struct list_head csi_chans;
};
diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
index 8c9655ffa886..d99a04fa25af 100644
--- a/drivers/staging/media/tegra-video/tegra20.c
+++ b/drivers/staging/media/tegra-video/tegra20.c
@@ -4,6 +4,9 @@
*
* Copyright (C) 2023 SKIDATA GmbH
* Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ *
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
*/
/*
@@ -12,10 +15,15 @@
*/
#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
#include <linux/delay.h>
#include <linux/host1x.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
+#include <linux/pm_runtime.h>
+#include <linux/tegra-mipi-cal.h>
#include <linux/v4l2-mediabus.h>
#include "vip.h"
@@ -42,6 +50,9 @@ enum tegra_vi_out {
#define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
#define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
+#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
+#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
+
#define TEGRA_VI_VI_INPUT_CONTROL 0x0088
#define VI_INPUT_FIELD_DETECT BIT(27)
#define VI_INPUT_BT656 BIT(25)
@@ -88,6 +99,8 @@ enum tegra_vi_out {
#define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
/* TEGRA_VI_OUT_2 supported formats */
+#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
+#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
#define TEGRA_VI_VIP_H_ACTIVE 0x00a4
@@ -152,8 +165,106 @@ enum tegra_vi_out {
#define TEGRA_VI_VI_RAISE 0x01ac
#define VI_VI_RAISE_ON_EDGE BIT(0)
+#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
+#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
+#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
+#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
+
+/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
+#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
+#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
+#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
+#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
+#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
+#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
+#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
+#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
+#define CSI_PP_HEADER_EC_ENABLE BIT(27)
+#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
+#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
+#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
+#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
+#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
+#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
+#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
+#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
+#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
+#define CSI_PP_DATA_TYPE(n) ((n) << 8)
+#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
+#define CSI_PP_WORD_COUNT_HEADER BIT(6)
+#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
+#define CSI_PP_PACKET_HEADER_SENT BIT(4)
+#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
+#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
+#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
+#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
+#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
+#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
+#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
+#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
+#define CSI_PP_VSYNC_START_MARKER BIT(4)
+#define CSI_PP_SINGLE_SHOT BIT(2)
+#define CSI_PP_NOP 0
+#define CSI_PP_ENABLE 1
+#define CSI_PP_DISABLE 2
+#define CSI_PP_RESET 3
+#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
+#define CSI_A_PHY_CIL_NOP 0x0
+#define CSI_A_PHY_CIL_ENABLE 0x1
+#define CSI_A_PHY_CIL_DISABLE 0x2
+#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
+#define CSI_B_PHY_CIL_NOP (0x0 << 16)
+#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
+#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
+#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
+#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
+#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
+#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
+#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
+#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
+#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
+#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
+#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
+#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
+#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
+#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
+#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
+#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
+#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
+#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
+#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
+#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
+#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
+#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
+#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
+#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
+#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
+#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
+#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
+#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
+#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
+#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
+#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
+#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
+#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
+#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
+#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
+#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
+#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
+#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
+#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
+#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
+#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
+#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
+#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
+#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
+#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
+#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
+
/* --------------------------------------------------------------------------
- * VI
+ * Read and Write helpers
*/
static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
@@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
writel(val, chan->vi->iomem + addr);
}
+static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
+{
+ return readl(chan->vi->iomem + addr);
+}
+
+static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
+{
+ writel(val, csi_chan->csi->iomem + addr);
+}
+
+static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
+{
+ return readl(csi_chan->csi->iomem + addr);
+}
+
+static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
+{
+ writel(val, csi->iomem + addr);
+}
+
+static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
+{
+ return readl(csi->iomem + addr);
+}
+
+/* --------------------------------------------------------------------------
+ * VI
+ */
+
/*
* Get the main input format (YUV/RGB...) and the YUV variant as values to
* be written into registers for the current VI input mbus code.
@@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
{
struct tegra_vi *vi = chan->vi;
- struct host1x_syncpt *out_sp;
+ struct host1x_syncpt *out_sp, *fs_sp;
out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
if (!out_sp)
- return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
+ return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
chan->mw_ack_sp[0] = out_sp;
+ fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
+ if (!fs_sp)
+ return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
+
+ chan->frame_start_sp[0] = fs_sp;
+
return 0;
}
static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
{
host1x_syncpt_put(chan->mw_ack_sp[0]);
+ host1x_syncpt_put(chan->frame_start_sp[0]);
}
static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
@@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
}
static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
- struct tegra_channel_buffer *buf)
+ struct tegra_channel_buffer *buf,
+ struct tegra_csi_channel *csi_chan)
{
+ u32 val;
int err;
- chan->next_out_sp_idx++;
-
tegra20_channel_vi_buffer_setup(chan, buf);
- tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
+ if (csi_chan) {
+ u32 port = csi_chan->csi_port_nums[0] & 1;
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
+
+ val = host1x_syncpt_read(chan->frame_start_sp[0]);
+ do {
+ err = host1x_syncpt_wait(chan->frame_start_sp[0],
+ val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
+ } while (err == -ERESTARTSYS);
+
+ if (err) {
+ if (err != -ERESTARTSYS)
+ dev_err_ratelimited(&chan->video.dev,
+ "frame start syncpt timeout: %d\n", err);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
+ goto exit;
+ }
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+ } else {
+ tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
+ }
+
+ val = host1x_syncpt_read(chan->mw_ack_sp[0]);
+ do {
+ err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
+ TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
+ } while (err == -ERESTARTSYS);
- /* Wait for syncpt counter to reach frame start event threshold */
- err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
- TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
if (err) {
- host1x_syncpt_incr(chan->mw_ack_sp[0]);
- dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
- release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
- return err;
+ if (err != -ERESTARTSYS)
+ dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
+ goto exit;
}
- tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
- VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
+ if (!csi_chan)
+ tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
+ VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
+exit:
release_buffer(chan, buf, VB2_BUF_STATE_DONE);
- return 0;
+ return err;
}
static int tegra20_chan_capture_kthread_start(void *data)
{
struct tegra_vi_channel *chan = data;
struct tegra_channel_buffer *buf;
+ struct v4l2_subdev *csi_subdev = NULL;
+ struct tegra_csi_channel *csi_chan = NULL;
unsigned int retries = 0;
int err = 0;
+ csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
+ if (csi_subdev)
+ csi_chan = to_csi_chan(csi_subdev);
+
while (1) {
/*
* Source is not streaming if error is non-zero.
@@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
list_del_init(&buf->queue);
spin_unlock(&chan->start_lock);
- err = tegra20_channel_capture_frame(chan, buf);
+ err = tegra20_channel_capture_frame(chan, buf, csi_chan);
if (!err) {
retries = 0;
continue;
@@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
data_type == TEGRA_IMAGE_DT_RAW10) ?
TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
- int main_output_format;
- int yuv_output_format;
-
- tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
-
- /*
- * Set up low pass filter. Use 0x240 for chromaticity and 0x240
- * for luminance, which is the default and means not to touch
- * anything.
- */
- tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
- 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
- 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
-
- /* Set up raise-on-edge, so we get an interrupt on end of frame. */
- tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
-
- tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
- (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
- (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
- yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
- main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
/* Set up frame size */
tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
@@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
struct media_pipeline *pipe = &chan->video.pipe;
int err;
- chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
-
err = video_device_pipeline_start(&chan->video, pipe);
if (err)
goto error_pipeline_start;
- tegra20_camera_capture_setup(chan);
+ /*
+ * Set up low pass filter. Use 0x240 for chromaticity and 0x240
+ * for luminance, which is the default and means not to touch
+ * anything.
+ */
+ tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
+ 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
+ 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
+
+ /* Set up raise-on-edge, so we get an interrupt on end of frame. */
+ tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
err = tegra_channel_set_stream(chan, true);
if (err)
goto error_set_stream;
+ tegra20_camera_capture_setup(chan);
+
chan->sequence = 0;
chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
@@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
.has_h_v_flip = true,
};
+/* --------------------------------------------------------------------------
+ * MIPI Calibration
+ */
+static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
+{
+ struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
+ unsigned int port = mipi->pads;
+ u32 value;
+ int ret;
+
+ guard(mutex)(&csi->mipi_lock);
+
+ ret = pm_runtime_resume_and_get(csi->dev);
+ if (ret < 0) {
+ dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
+ return ret;
+ }
+
+ tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_HSPDOS(4) |
+ CSI_CIL_MIPI_CAL_HSPUOS(3) |
+ CSI_CIL_MIPI_CAL_TERMOS(0));
+ tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
+ CSI_PAD_DRIV_DN_REF(5) |
+ CSI_PAD_DRIV_UP_REF(7) |
+ CSI_PAD_TERM_REF(0));
+
+ /* CSI B */
+ value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4);
+
+ if (port == PORT_B)
+ value |= CSI_CIL_MIPI_CAL_SEL_B;
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
+
+ /* CSI A */
+ value = CSI_CIL_MIPI_CAL_STARTCAL |
+ CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
+ CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4);
+
+ if (port == PORT_A)
+ value |= CSI_CIL_MIPI_CAL_SEL_A;
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
+
+ return 0;
+}
+
+static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
+{
+ struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
+ void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
+ unsigned int port = mipi->pads;
+ u32 value, pp = 0, cil = 0;
+ int ret;
+
+ /* This part is only for CSI */
+ if (port > PORT_B) {
+ pm_runtime_put(csi->dev);
+
+ return 0;
+ }
+
+ guard(mutex)(&csi->mipi_lock);
+
+ ret = readl_relaxed_poll_timeout(cil_status_reg, value,
+ value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
+ if (ret < 0) {
+ dev_warn(csi->dev, "MIPI calibration timeout!\n");
+ goto exit;
+ }
+
+ /* clear status */
+ tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
+ ret = readl_relaxed_poll_timeout(cil_status_reg, value,
+ !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
+ if (ret < 0) {
+ dev_warn(csi->dev, "MIPI calibration status timeout!\n");
+ goto exit;
+ }
+
+ pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
+ cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
+ if (pp | cil) {
+ dev_warn(csi->dev, "Calibration status not been cleared!\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+exit:
+ tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
+
+ /* un-select to avoid interference with DSI */
+ tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4));
+
+ tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
+ CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
+ CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
+ CSI_CIL_MIPI_CAL_HSPDOS(0) |
+ CSI_CIL_MIPI_CAL_HSPUOS(0) |
+ CSI_CIL_MIPI_CAL_TERMOS(4));
+
+ pm_runtime_put(csi->dev);
+
+ return ret;
+}
+
+static const struct tegra_mipi_ops tegra20_mipi_ops = {
+ .start_calibration = tegra20_start_pad_calibration,
+ .finish_calibration = tegra20_finish_pad_calibration,
+};
+
+/* --------------------------------------------------------------------------
+ * CSI
+ */
+static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
+{
+ tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
+ CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
+}
+
+static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
+ u8 portno)
+{
+ struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
+ int width = vi_chan->format.width;
+ int height = vi_chan->format.height;
+ u32 data_type = vi_chan->fmtinfo->img_dt;
+ u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
+ enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
+
+ unsigned int main_output_format, yuv_output_format;
+ unsigned int port = portno & 1;
+ u32 value;
+
+ tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
+
+ switch (data_type) {
+ case TEGRA_IMAGE_DT_RAW8:
+ case TEGRA_IMAGE_DT_RAW10:
+ output_channel = TEGRA_VI_OUT_2;
+ if (port == PORT_A)
+ main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
+ else
+ main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
+ break;
+ }
+
+ tegra20_csi_capture_clean(csi_chan);
+
+ /* CSI port cleanup */
+ tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
+ tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
+ tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
+ CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
+ CSI_PP_EXP_FRAME_HEIGHT(height) |
+ CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
+ CSI_PP_LINE_TIMEOUT_ENABLE);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
+ CSI_PP_OUTPUT_FORMAT_PIXEL |
+ CSI_PP_DATA_TYPE(data_type) |
+ CSI_PP_CRC_CHECK_ENABLE |
+ CSI_PP_WORD_COUNT_HEADER |
+ CSI_PP_DATA_IDENTIFIER_ENABLE |
+ CSI_PP_PACKET_HEADER_SENT |
+ port);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
+ CSI_SKIP_PACKET_THRESHOLD(0x3f) |
+ (csi_chan->numlanes - 1));
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
+ CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
+ 0x5); /* Clock settle time */
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
+ host1x_syncpt_id(vi_chan->frame_start_sp[0])
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
+ VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
+ host1x_syncpt_id(vi_chan->mw_ack_sp[0])
+ << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
+
+ value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
+ CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
+ (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
+ (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
+ yuv_output_format | main_output_format);
+
+ return 0;
+};
+
+static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
+{
+ struct tegra_csi *csi = csi_chan->csi;
+ unsigned int port = portno & 1;
+ u32 value;
+
+ value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
+ dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
+
+ value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
+ dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
+ tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
+
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
+ CSI_PP_START_MARKER_FRAME_MAX(0xf) |
+ CSI_PP_DISABLE);
+
+ if (csi_chan->numlanes == 4) {
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
+ CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
+ } else {
+ value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
+ CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
+ tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
+ }
+}
+
+static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
+{
+ u8 *portnos = csi_chan->csi_port_nums;
+ int ret, i;
+
+ for (i = 0; i < csi_chan->numgangports; i++) {
+ ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
+ if (ret)
+ goto stream_start_fail;
+ }
+
+ return 0;
+
+stream_start_fail:
+ for (i = i - 1; i >= 0; i--)
+ tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
+
+ return ret;
+}
+
+static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
+{
+ u8 *portnos = csi_chan->csi_port_nums;
+ int i;
+
+ for (i = 0; i < csi_chan->numgangports; i++)
+ tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
+}
+
+static const struct tegra_csi_ops tegra20_csi_ops = {
+ .csi_start_streaming = tegra20_csi_start_streaming,
+ .csi_stop_streaming = tegra20_csi_stop_streaming,
+};
+
+static const char * const tegra20_csi_clks[] = {
+ NULL,
+};
+
+const struct tegra_csi_soc tegra20_csi_soc = {
+ .ops = &tegra20_csi_ops,
+ .mipi_ops = &tegra20_mipi_ops,
+ .csi_max_channels = 2, /* CSI-A and CSI-B */
+ .clk_names = tegra20_csi_clks,
+ .num_clks = ARRAY_SIZE(tegra20_csi_clks),
+};
+
+static const char * const tegra30_csi_clks[] = {
+ "csi",
+ "csia-pad",
+ "csib-pad",
+};
+
+const struct tegra_csi_soc tegra30_csi_soc = {
+ .ops = &tegra20_csi_ops,
+ .mipi_ops = &tegra20_mipi_ops,
+ .csi_max_channels = 2, /* CSI-A and CSI-B */
+ .clk_names = tegra30_csi_clks,
+ .num_clks = ARRAY_SIZE(tegra30_csi_clks),
+};
+
/* --------------------------------------------------------------------------
* VIP
*/
@@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
data_type == TEGRA_IMAGE_DT_RAW10) ?
TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
- unsigned int main_input_format;
- unsigned int yuv_input_format;
+ unsigned int main_input_format, yuv_input_format;
+ unsigned int main_output_format, yuv_output_format;
tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
+ tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
@@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
+ tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
+ (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
+ (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
+ yuv_output_format | main_output_format);
+
return 0;
}
diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
index 5cbc0606ed6c..bad55e0bd313 100644
--- a/drivers/staging/media/tegra-video/vi.h
+++ b/drivers/staging/media/tegra-video/vi.h
@@ -125,7 +125,6 @@ struct tegra_vi {
* frame through host1x syncpoint counters (On Tegra20 used for the
* OUT_1 syncpt)
* @sp_incr_lock: protects cpu syncpoint increment.
- * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
*
* @kthread_start_capture: kthread to start capture of single frame when
* vb buffer is available. This thread programs VI CSI hardware
@@ -188,7 +187,6 @@ struct tegra_vi_channel {
struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
/* protects the cpu syncpoint increment */
spinlock_t sp_incr_lock[GANG_PORTS_MAX];
- u32 next_out_sp_idx;
struct task_struct *kthread_start_capture;
wait_queue_head_t start_wait;
diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
index 6fe8d5301b9c..9f2bddc460bf 100644
--- a/drivers/staging/media/tegra-video/video.c
+++ b/drivers/staging/media/tegra-video/video.c
@@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
{ .compatible = "nvidia,tegra20-vip", },
{ .compatible = "nvidia,tegra20-vi", },
#endif
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ { .compatible = "nvidia,tegra20-csi", },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ { .compatible = "nvidia,tegra30-csi", },
+#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{ .compatible = "nvidia,tegra210-csi", },
{ .compatible = "nvidia,tegra210-vi", },
--
2.48.1
^ permalink raw reply related [flat|nested] 50+ messages in thread
* Re: [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114
2025-09-25 15:16 ` [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114 Svyatoslav Ryhel
@ 2025-10-01 4:02 ` Mikko Perttunen
0 siblings, 0 replies; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 4:02 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Svyatoslav Ryhel, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann,
Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> The CSUS clock is a clock gate for the output clock signal primarily
> sourced from the VI_SENSOR clock. This clock signal is used as an input
> MCLK clock for cameras.
>
> Unlike later Tegra SoCs, the Tegra 20 can change its CSUS parent, which is
> why csus_mux is added in a similar way to how CDEV1 and CDEV2 are handled.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> drivers/clk/tegra/clk-tegra114.c | 7 ++++++-
> drivers/clk/tegra/clk-tegra20.c | 20 +++++++++++++-------
> drivers/clk/tegra/clk-tegra30.c | 7 ++++++-
> drivers/pinctrl/tegra/pinctrl-tegra20.c | 7 +++++++
> 4 files changed, 32 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
> index 186b0b81c1ec..00282b0d3763 100644
> --- a/drivers/clk/tegra/clk-tegra114.c
> +++ b/drivers/clk/tegra/clk-tegra114.c
> @@ -691,7 +691,6 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = {
> [tegra_clk_tsec] = { .dt_id = TEGRA114_CLK_TSEC, .present = true },
> [tegra_clk_xusb_host] = { .dt_id = TEGRA114_CLK_XUSB_HOST, .present = true },
> [tegra_clk_msenc] = { .dt_id = TEGRA114_CLK_MSENC, .present = true },
> - [tegra_clk_csus] = { .dt_id = TEGRA114_CLK_CSUS, .present = true },
> [tegra_clk_mselect] = { .dt_id = TEGRA114_CLK_MSELECT, .present = true },
> [tegra_clk_tsensor] = { .dt_id = TEGRA114_CLK_TSENSOR, .present = true },
> [tegra_clk_i2s3] = { .dt_id = TEGRA114_CLK_I2S3, .present = true },
> @@ -1047,6 +1046,12 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base,
> 0, 82, periph_clk_enb_refcnt);
> clks[TEGRA114_CLK_DSIB] = clk;
>
> + /* csus */
> + clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
> + clk_base, 0, TEGRA114_CLK_CSUS,
> + periph_clk_enb_refcnt);
> + clks[TEGRA114_CLK_CSUS] = clk;
> +
> /* emc mux */
> clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
> ARRAY_SIZE(mux_pllmcp_clkm),
> diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
> index 2c58ce25af75..d8d5afeb6f9b 100644
> --- a/drivers/clk/tegra/clk-tegra20.c
> +++ b/drivers/clk/tegra/clk-tegra20.c
> @@ -530,7 +530,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
> [tegra_clk_rtc] = { .dt_id = TEGRA20_CLK_RTC, .present = true },
> [tegra_clk_timer] = { .dt_id = TEGRA20_CLK_TIMER, .present = true },
> [tegra_clk_kbc] = { .dt_id = TEGRA20_CLK_KBC, .present = true },
> - [tegra_clk_csus] = { .dt_id = TEGRA20_CLK_CSUS, .present = true },
> [tegra_clk_vcp] = { .dt_id = TEGRA20_CLK_VCP, .present = true },
> [tegra_clk_bsea] = { .dt_id = TEGRA20_CLK_BSEA, .present = true },
> [tegra_clk_bsev] = { .dt_id = TEGRA20_CLK_BSEV, .present = true },
> @@ -834,6 +833,12 @@ static void __init tegra20_periph_clk_init(void)
> clk_base, 0, 93, periph_clk_enb_refcnt);
> clks[TEGRA20_CLK_CDEV2] = clk;
>
> + /* csus */
> + clk = tegra_clk_register_periph_gate("csus", "csus_mux", 0,
> + clk_base, 0, TEGRA20_CLK_CSUS,
> + periph_clk_enb_refcnt);
> + clks[TEGRA20_CLK_CSUS] = clk;
> +
> for (i = 0; i < ARRAY_SIZE(tegra_periph_clk_list); i++) {
> data = &tegra_periph_clk_list[i];
> clk = tegra_clk_register_periph_data(clk_base, data);
> @@ -1093,14 +1098,15 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
> hw = __clk_get_hw(clk);
>
> /*
> - * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
> - * clock is created by the pinctrl driver. It is possible for clk user
> - * to request these clocks before pinctrl driver got probed and hence
> - * user will get an orphaned clock. That might be undesirable because
> - * user may expect parent clock to be enabled by the child.
> + * Tegra20 CDEV1, CDEV2 and CSUS clocks are a bit special case, their
> + * parent clock is created by the pinctrl driver. It is possible for
> + * clk user to request these clocks before pinctrl driver got probed
> + * and hence user will get an orphaned clock. That might be undesirable
> + * because user may expect parent clock to be enabled by the child.
> */
> if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
> - clkspec->args[0] == TEGRA20_CLK_CDEV2) {
> + clkspec->args[0] == TEGRA20_CLK_CDEV2 ||
> + clkspec->args[0] == TEGRA20_CLK_CSUS) {
> parent_hw = clk_hw_get_parent(hw);
> if (!parent_hw)
> return ERR_PTR(-EPROBE_DEFER);
> diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
> index 82a8cb9545eb..ca367184e185 100644
> --- a/drivers/clk/tegra/clk-tegra30.c
> +++ b/drivers/clk/tegra/clk-tegra30.c
> @@ -779,7 +779,6 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
> [tegra_clk_rtc] = { .dt_id = TEGRA30_CLK_RTC, .present = true },
> [tegra_clk_timer] = { .dt_id = TEGRA30_CLK_TIMER, .present = true },
> [tegra_clk_kbc] = { .dt_id = TEGRA30_CLK_KBC, .present = true },
> - [tegra_clk_csus] = { .dt_id = TEGRA30_CLK_CSUS, .present = true },
> [tegra_clk_vcp] = { .dt_id = TEGRA30_CLK_VCP, .present = true },
> [tegra_clk_bsea] = { .dt_id = TEGRA30_CLK_BSEA, .present = true },
> [tegra_clk_bsev] = { .dt_id = TEGRA30_CLK_BSEV, .present = true },
> @@ -1008,6 +1007,12 @@ static void __init tegra30_periph_clk_init(void)
> 0, 48, periph_clk_enb_refcnt);
> clks[TEGRA30_CLK_DSIA] = clk;
>
> + /* csus */
> + clk = tegra_clk_register_periph_gate("csus", "vi_sensor", 0,
> + clk_base, 0, TEGRA30_CLK_CSUS,
> + periph_clk_enb_refcnt);
> + clks[TEGRA30_CLK_CSUS] = clk;
> +
> /* pcie */
> clk = tegra_clk_register_periph_gate("pcie", "clk_m", 0, clk_base, 0,
> 70, periph_clk_enb_refcnt);
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra20.c b/drivers/pinctrl/tegra/pinctrl-tegra20.c
> index 737fc2000f66..437e0ac091cc 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra20.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra20.c
> @@ -2230,6 +2230,10 @@ static const char *cdev2_parents[] = {
> "dev2_osc_div", "hclk", "pclk", "pll_p_out4",
> };
>
> +static const char *csus_parents[] = {
> + "pll_c_out1", "pll_p_out2", "pll_p_out3", "vi_sensor",
> +};
> +
> static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev)
> {
> struct tegra_pmx *pmx = platform_get_drvdata(pdev);
> @@ -2239,6 +2243,9 @@ static void tegra20_pinctrl_register_clock_muxes(struct platform_device *pdev)
>
> clk_register_mux(NULL, "cdev2_mux", cdev2_parents, 4, 0,
> pmx->regs[1] + 0x8, 4, 2, CLK_MUX_READ_ONLY, NULL);
> +
> + clk_register_mux(NULL, "csus_mux", csus_parents, 4, 0,
> + pmx->regs[1] + 0x8, 6, 2, CLK_MUX_READ_ONLY, NULL);
> }
>
> static int tegra20_pinctrl_probe(struct platform_device *pdev)
>
Please move the pinctrl changes to a separate patch. Otherwise, LGTM.
Mikko
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers
2025-09-25 15:16 ` [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers Svyatoslav Ryhel
@ 2025-10-01 4:18 ` Mikko Perttunen
0 siblings, 0 replies; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 4:18 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Svyatoslav Ryhel, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann,
Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> Convert existing MIPI code to use operation function pointers, a necessary
> step for supporting Tegra20/Tegra30 SoCs. All common MIPI configuration
> that is SoC-independent remains in mipi.c, while all SoC-specific code is
> moved to tegra114-mipi.c (The naming matches the first SoC generation with
> a dedicated calibration block). Shared structures and function calls are
> placed into tegra-mipi-cal.h.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> drivers/gpu/drm/tegra/dsi.c | 1 +
> drivers/gpu/host1x/Makefile | 1 +
> drivers/gpu/host1x/dev.c | 2 +
> drivers/gpu/host1x/dev.h | 2 +
> drivers/gpu/host1x/mipi.c | 501 +++---------------------
> drivers/gpu/host1x/tegra114-mipi.c | 483 +++++++++++++++++++++++
> drivers/staging/media/tegra-video/csi.c | 1 +
> include/linux/host1x.h | 10 -
> include/linux/tegra-mipi-cal.h | 56 +++
> 9 files changed, 594 insertions(+), 463 deletions(-)
> create mode 100644 drivers/gpu/host1x/tegra114-mipi.c
> create mode 100644 include/linux/tegra-mipi-cal.h
>
> diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
> index 64f12a85a9dd..278bf2c85524 100644
> --- a/drivers/gpu/drm/tegra/dsi.c
> +++ b/drivers/gpu/drm/tegra/dsi.c
> @@ -14,6 +14,7 @@
> #include <linux/pm_runtime.h>
> #include <linux/regulator/consumer.h>
> #include <linux/reset.h>
> +#include <linux/tegra-mipi-cal.h>
>
> #include <video/mipi_display.h>
>
> diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
> index ee5286ffe08d..fead483af0b4 100644
> --- a/drivers/gpu/host1x/Makefile
> +++ b/drivers/gpu/host1x/Makefile
> @@ -9,6 +9,7 @@ host1x-y = \
> job.o \
> debug.o \
> mipi.o \
> + tegra114-mipi.o \
> fence.o \
> hw/host1x01.o \
> hw/host1x02.o \
> diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
> index 1f93e5e276c0..6c403c8e867b 100644
> --- a/drivers/gpu/host1x/dev.c
> +++ b/drivers/gpu/host1x/dev.c
> @@ -782,7 +782,9 @@ static struct platform_driver tegra_host1x_driver = {
>
> static struct platform_driver * const drivers[] = {
> &tegra_host1x_driver,
> +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
> &tegra_mipi_driver,
> +#endif
This will disable the driver on multi-SoC configurations like multi_v7_defconfig. We can build in both providers, they just won't be both be probed on any one SoC.
Otherwise looks good.
> };
>
> static int __init tegra_host1x_init(void)
> diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
> index d3855a1c6b47..9be9669d3b1c 100644
> --- a/drivers/gpu/host1x/dev.h
> +++ b/drivers/gpu/host1x/dev.h
> @@ -354,6 +354,8 @@ static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
> host->debug_op->show_mlocks(host, o);
> }
>
> +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
> extern struct platform_driver tegra_mipi_driver;
> +#endif
>
> #endif
> diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
> index e51b43dd15a3..1695e6f3b0d0 100644
> --- a/drivers/gpu/host1x/mipi.c
> +++ b/drivers/gpu/host1x/mipi.c
> @@ -21,195 +21,60 @@
> */
>
> #include <linux/clk.h>
> -#include <linux/host1x.h>
> #include <linux/io.h>
> #include <linux/iopoll.h>
> +#include <linux/of.h>
> #include <linux/of_platform.h>
> #include <linux/platform_device.h>
> #include <linux/slab.h>
> +#include <linux/tegra-mipi-cal.h>
>
> -#include "dev.h"
> +/* only need to support one provider */
> +static struct {
> + struct device_node *np;
> + const struct tegra_mipi_ops *ops;
> +} provider;
>
> -#define MIPI_CAL_CTRL 0x00
> -#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
> -#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
> -#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
> -#define MIPI_CAL_CTRL_START (1 << 0)
> -
> -#define MIPI_CAL_AUTOCAL_CTRL 0x01
> -
> -#define MIPI_CAL_STATUS 0x02
> -#define MIPI_CAL_STATUS_DONE (1 << 16)
> -#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
> -
> -#define MIPI_CAL_CONFIG_CSIA 0x05
> -#define MIPI_CAL_CONFIG_CSIB 0x06
> -#define MIPI_CAL_CONFIG_CSIC 0x07
> -#define MIPI_CAL_CONFIG_CSID 0x08
> -#define MIPI_CAL_CONFIG_CSIE 0x09
> -#define MIPI_CAL_CONFIG_CSIF 0x0a
> -#define MIPI_CAL_CONFIG_DSIA 0x0e
> -#define MIPI_CAL_CONFIG_DSIB 0x0f
> -#define MIPI_CAL_CONFIG_DSIC 0x10
> -#define MIPI_CAL_CONFIG_DSID 0x11
> -
> -#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
> -#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
> -#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
> -#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
> -#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
> -#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
> -#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
> -
> -/* for data and clock lanes */
> -#define MIPI_CAL_CONFIG_SELECT (1 << 21)
> -
> -/* for data lanes */
> -#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
> -#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
> -#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
> -
> -/* for clock lanes */
> -#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
> -#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
> -
> -#define MIPI_CAL_BIAS_PAD_CFG0 0x16
> -#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
> -#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
> -
> -#define MIPI_CAL_BIAS_PAD_CFG1 0x17
> -#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
> -#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
> -
> -#define MIPI_CAL_BIAS_PAD_CFG2 0x18
> -#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
> -#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
> -#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
> -
> -struct tegra_mipi_pad {
> - unsigned long data;
> - unsigned long clk;
> -};
> -
> -struct tegra_mipi_soc {
> - bool has_clk_lane;
> - const struct tegra_mipi_pad *pads;
> - unsigned int num_pads;
> -
> - bool clock_enable_override;
> - bool needs_vclamp_ref;
> -
> - /* bias pad configuration settings */
> - u8 pad_drive_down_ref;
> - u8 pad_drive_up_ref;
> -
> - u8 pad_vclamp_level;
> - u8 pad_vauxp_level;
> -
> - /* calibration settings for data lanes */
> - u8 hspdos;
> - u8 hspuos;
> - u8 termos;
> -
> - /* calibration settings for clock lanes */
> - u8 hsclkpdos;
> - u8 hsclkpuos;
> -};
> -
> -struct tegra_mipi {
> - const struct tegra_mipi_soc *soc;
> - struct device *dev;
> - void __iomem *regs;
> - struct mutex lock;
> - struct clk *clk;
> -
> - unsigned long usage_count;
> -};
> -
> -struct tegra_mipi_device {
> - struct platform_device *pdev;
> - struct tegra_mipi *mipi;
> - struct device *device;
> - unsigned long pads;
> -};
> -
> -static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
> - unsigned long offset)
> +int tegra_mipi_enable(struct tegra_mipi_device *device)
> {
> - return readl(mipi->regs + (offset << 2));
> -}
> + if (device->ops->enable)
> + return device->ops->enable(device);
>
> -static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
> - unsigned long offset)
> -{
> - writel(value, mipi->regs + (offset << 2));
> + return 0;
> }
> +EXPORT_SYMBOL(tegra_mipi_enable);
>
> -static int tegra_mipi_power_up(struct tegra_mipi *mipi)
> +int tegra_mipi_disable(struct tegra_mipi_device *device)
> {
> - u32 value;
> - int err;
> -
> - err = clk_enable(mipi->clk);
> - if (err < 0)
> - return err;
> -
> - value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
> - value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
> -
> - if (mipi->soc->needs_vclamp_ref)
> - value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
> -
> - tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
> -
> - value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
> - value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
> - tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> -
> - clk_disable(mipi->clk);
> + if (device->ops->disable)
> + return device->ops->disable(device);
>
> return 0;
> }
> +EXPORT_SYMBOL(tegra_mipi_disable);
>
> -static int tegra_mipi_power_down(struct tegra_mipi *mipi)
> +int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
> {
> - u32 value;
> - int err;
> + if (device->ops->start_calibration)
> + return device->ops->start_calibration(device);
>
> - err = clk_enable(mipi->clk);
> - if (err < 0)
> - return err;
> -
> - /*
> - * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
> - * supplies the DSI pads. This must be kept enabled until none of the
> - * DSI lanes are used anymore.
> - */
> - value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
> - value |= MIPI_CAL_BIAS_PAD_PDVREG;
> - tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> -
> - /*
> - * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
> - * control a regulator that supplies current to the pre-driver logic.
> - * Powering down this regulator causes DSI to fail, so it must remain
> - * powered on until none of the DSI lanes are used anymore.
> - */
> - value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
> -
> - if (mipi->soc->needs_vclamp_ref)
> - value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
> + return 0;
> +}
> +EXPORT_SYMBOL(tegra_mipi_start_calibration);
>
> - value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
> - tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
> +int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
> +{
> + if (device->ops->finish_calibration)
> + return device->ops->finish_calibration(device);
>
> return 0;
> }
> +EXPORT_SYMBOL(tegra_mipi_finish_calibration);
>
> struct tegra_mipi_device *tegra_mipi_request(struct device *device,
> struct device_node *np)
> {
> - struct tegra_mipi_device *dev;
> + struct tegra_mipi_device *mipidev;
> struct of_phandle_args args;
> int err;
>
> @@ -219,321 +84,51 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
> if (err < 0)
> return ERR_PTR(err);
>
> - dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> - if (!dev) {
> + if (provider.np != args.np)
> + return ERR_PTR(-ENODEV);
> +
> + mipidev = kzalloc(sizeof(*mipidev), GFP_KERNEL);
> + if (!mipidev) {
> err = -ENOMEM;
> goto out;
> }
>
> - dev->pdev = of_find_device_by_node(args.np);
> - if (!dev->pdev) {
> + mipidev->pdev = of_find_device_by_node(args.np);
> + if (!mipidev->pdev) {
> err = -ENODEV;
> goto free;
> }
>
> - dev->mipi = platform_get_drvdata(dev->pdev);
> - if (!dev->mipi) {
> - err = -EPROBE_DEFER;
> - goto put;
> - }
> -
> of_node_put(args.np);
>
> - dev->pads = args.args[0];
> - dev->device = device;
> + mipidev->ops = provider.ops;
> + mipidev->pads = args.args[0];
>
> - return dev;
> + return mipidev;
>
> -put:
> - platform_device_put(dev->pdev);
> free:
> - kfree(dev);
> + kfree(mipidev);
> out:
> of_node_put(args.np);
> return ERR_PTR(err);
> }
> EXPORT_SYMBOL(tegra_mipi_request);
>
> -void tegra_mipi_free(struct tegra_mipi_device *device)
> +void tegra_mipi_free(struct tegra_mipi_device *mipidev)
> {
> - platform_device_put(device->pdev);
> - kfree(device);
> + platform_device_put(mipidev->pdev);
> + kfree(mipidev);
> }
> EXPORT_SYMBOL(tegra_mipi_free);
>
> -int tegra_mipi_enable(struct tegra_mipi_device *dev)
> -{
> - int err = 0;
> -
> - mutex_lock(&dev->mipi->lock);
> -
> - if (dev->mipi->usage_count++ == 0)
> - err = tegra_mipi_power_up(dev->mipi);
> -
> - mutex_unlock(&dev->mipi->lock);
> -
> - return err;
> -
> -}
> -EXPORT_SYMBOL(tegra_mipi_enable);
> -
> -int tegra_mipi_disable(struct tegra_mipi_device *dev)
> +int tegra_mipi_add_provider(struct device_node *np, const struct tegra_mipi_ops *ops)
> {
> - int err = 0;
> + if (provider.np)
> + return -EBUSY;
>
> - mutex_lock(&dev->mipi->lock);
> -
> - if (--dev->mipi->usage_count == 0)
> - err = tegra_mipi_power_down(dev->mipi);
> -
> - mutex_unlock(&dev->mipi->lock);
> -
> - return err;
> -
> -}
> -EXPORT_SYMBOL(tegra_mipi_disable);
> -
> -int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
> -{
> - struct tegra_mipi *mipi = device->mipi;
> - void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
> - u32 value;
> - int err;
> -
> - err = readl_relaxed_poll_timeout(status_reg, value,
> - !(value & MIPI_CAL_STATUS_ACTIVE) &&
> - (value & MIPI_CAL_STATUS_DONE), 50,
> - 250000);
> - mutex_unlock(&device->mipi->lock);
> - clk_disable(device->mipi->clk);
> -
> - return err;
> -}
> -EXPORT_SYMBOL(tegra_mipi_finish_calibration);
> -
> -int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
> -{
> - const struct tegra_mipi_soc *soc = device->mipi->soc;
> - unsigned int i;
> - u32 value;
> - int err;
> -
> - err = clk_enable(device->mipi->clk);
> - if (err < 0)
> - return err;
> -
> - mutex_lock(&device->mipi->lock);
> -
> - value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
> - MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
> - tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
> -
> - value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
> - value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
> - value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
> - value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
> - value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
> - tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> -
> - for (i = 0; i < soc->num_pads; i++) {
> - u32 clk = 0, data = 0;
> -
> - if (device->pads & BIT(i)) {
> - data = MIPI_CAL_CONFIG_SELECT |
> - MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
> - MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
> - MIPI_CAL_CONFIG_TERMOS(soc->termos);
> - clk = MIPI_CAL_CONFIG_SELECT |
> - MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
> - MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
> - }
> -
> - tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
> -
> - if (soc->has_clk_lane && soc->pads[i].clk != 0)
> - tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
> - }
> -
> - value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
> - value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
> - value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
> - value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
> - value |= MIPI_CAL_CTRL_PRESCALE(0x2);
> -
> - if (!soc->clock_enable_override)
> - value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
> - else
> - value |= MIPI_CAL_CTRL_CLKEN_OVR;
> -
> - tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
> -
> - /* clear any pending status bits */
> - value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
> - tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
> -
> - value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
> - value |= MIPI_CAL_CTRL_START;
> - tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
> -
> - /*
> - * Wait for min 72uS to let calibration logic finish calibration
> - * sequence codes before waiting for pads idle state to apply the
> - * results.
> - */
> - usleep_range(75, 80);
> -
> - return 0;
> -}
> -EXPORT_SYMBOL(tegra_mipi_start_calibration);
> -
> -static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
> - { .data = MIPI_CAL_CONFIG_CSIA },
> - { .data = MIPI_CAL_CONFIG_CSIB },
> - { .data = MIPI_CAL_CONFIG_CSIC },
> - { .data = MIPI_CAL_CONFIG_CSID },
> - { .data = MIPI_CAL_CONFIG_CSIE },
> - { .data = MIPI_CAL_CONFIG_DSIA },
> - { .data = MIPI_CAL_CONFIG_DSIB },
> - { .data = MIPI_CAL_CONFIG_DSIC },
> - { .data = MIPI_CAL_CONFIG_DSID },
> -};
> -
> -static const struct tegra_mipi_soc tegra114_mipi_soc = {
> - .has_clk_lane = false,
> - .pads = tegra114_mipi_pads,
> - .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
> - .clock_enable_override = true,
> - .needs_vclamp_ref = true,
> - .pad_drive_down_ref = 0x2,
> - .pad_drive_up_ref = 0x0,
> - .pad_vclamp_level = 0x0,
> - .pad_vauxp_level = 0x0,
> - .hspdos = 0x0,
> - .hspuos = 0x4,
> - .termos = 0x5,
> - .hsclkpdos = 0x0,
> - .hsclkpuos = 0x4,
> -};
> -
> -static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
> - { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
> - { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
> - { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
> - { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
> - { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
> - { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
> - { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
> -};
> -
> -static const struct tegra_mipi_soc tegra124_mipi_soc = {
> - .has_clk_lane = true,
> - .pads = tegra124_mipi_pads,
> - .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
> - .clock_enable_override = true,
> - .needs_vclamp_ref = true,
> - .pad_drive_down_ref = 0x2,
> - .pad_drive_up_ref = 0x0,
> - .pad_vclamp_level = 0x0,
> - .pad_vauxp_level = 0x0,
> - .hspdos = 0x0,
> - .hspuos = 0x0,
> - .termos = 0x0,
> - .hsclkpdos = 0x1,
> - .hsclkpuos = 0x2,
> -};
> -
> -static const struct tegra_mipi_soc tegra132_mipi_soc = {
> - .has_clk_lane = true,
> - .pads = tegra124_mipi_pads,
> - .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
> - .clock_enable_override = false,
> - .needs_vclamp_ref = false,
> - .pad_drive_down_ref = 0x0,
> - .pad_drive_up_ref = 0x3,
> - .pad_vclamp_level = 0x0,
> - .pad_vauxp_level = 0x0,
> - .hspdos = 0x0,
> - .hspuos = 0x0,
> - .termos = 0x0,
> - .hsclkpdos = 0x3,
> - .hsclkpuos = 0x2,
> -};
> -
> -static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
> - { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
> - { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
> - { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
> - { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
> - { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
> -};
> -
> -static const struct tegra_mipi_soc tegra210_mipi_soc = {
> - .has_clk_lane = true,
> - .pads = tegra210_mipi_pads,
> - .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
> - .clock_enable_override = true,
> - .needs_vclamp_ref = false,
> - .pad_drive_down_ref = 0x0,
> - .pad_drive_up_ref = 0x3,
> - .pad_vclamp_level = 0x1,
> - .pad_vauxp_level = 0x1,
> - .hspdos = 0x0,
> - .hspuos = 0x2,
> - .termos = 0x0,
> - .hsclkpdos = 0x0,
> - .hsclkpuos = 0x2,
> -};
> -
> -static const struct of_device_id tegra_mipi_of_match[] = {
> - { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
> - { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
> - { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
> - { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
> - { },
> -};
> -
> -static int tegra_mipi_probe(struct platform_device *pdev)
> -{
> - const struct of_device_id *match;
> - struct tegra_mipi *mipi;
> -
> - match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
> - if (!match)
> - return -ENODEV;
> -
> - mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
> - if (!mipi)
> - return -ENOMEM;
> -
> - mipi->soc = match->data;
> - mipi->dev = &pdev->dev;
> -
> - mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
> - if (IS_ERR(mipi->regs))
> - return PTR_ERR(mipi->regs);
> -
> - mutex_init(&mipi->lock);
> -
> - mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
> - if (IS_ERR(mipi->clk)) {
> - dev_err(&pdev->dev, "failed to get clock\n");
> - return PTR_ERR(mipi->clk);
> - }
> -
> - platform_set_drvdata(pdev, mipi);
> + provider.np = np;
> + provider.ops = ops;
>
> return 0;
> }
> -
> -struct platform_driver tegra_mipi_driver = {
> - .driver = {
> - .name = "tegra-mipi",
> - .of_match_table = tegra_mipi_of_match,
> - },
> - .probe = tegra_mipi_probe,
> -};
> +EXPORT_SYMBOL(tegra_mipi_add_provider);
> diff --git a/drivers/gpu/host1x/tegra114-mipi.c b/drivers/gpu/host1x/tegra114-mipi.c
> new file mode 100644
> index 000000000000..158a0491f830
> --- /dev/null
> +++ b/drivers/gpu/host1x/tegra114-mipi.c
> @@ -0,0 +1,483 @@
> +/*
> + * Copyright (C) 2013 NVIDIA Corporation
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission. The copyright holders make no representations
> + * about the suitability of this software for any purpose. It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/host1x.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/tegra-mipi-cal.h>
> +
> +#include "dev.h"
> +
> +#define MIPI_CAL_CTRL 0x00
> +#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
> +#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
> +#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
> +#define MIPI_CAL_CTRL_START (1 << 0)
> +
> +#define MIPI_CAL_AUTOCAL_CTRL 0x01
> +
> +#define MIPI_CAL_STATUS 0x02
> +#define MIPI_CAL_STATUS_DONE (1 << 16)
> +#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
> +
> +#define MIPI_CAL_CONFIG_CSIA 0x05
> +#define MIPI_CAL_CONFIG_CSIB 0x06
> +#define MIPI_CAL_CONFIG_CSIC 0x07
> +#define MIPI_CAL_CONFIG_CSID 0x08
> +#define MIPI_CAL_CONFIG_CSIE 0x09
> +#define MIPI_CAL_CONFIG_CSIF 0x0a
> +#define MIPI_CAL_CONFIG_DSIA 0x0e
> +#define MIPI_CAL_CONFIG_DSIB 0x0f
> +#define MIPI_CAL_CONFIG_DSIC 0x10
> +#define MIPI_CAL_CONFIG_DSID 0x11
> +
> +#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
> +#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
> +#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
> +#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
> +#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
> +#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
> +#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
> +
> +/* for data and clock lanes */
> +#define MIPI_CAL_CONFIG_SELECT (1 << 21)
> +
> +/* for data lanes */
> +#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
> +#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
> +#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
> +
> +/* for clock lanes */
> +#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
> +#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
> +
> +#define MIPI_CAL_BIAS_PAD_CFG0 0x16
> +#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
> +#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
> +
> +#define MIPI_CAL_BIAS_PAD_CFG1 0x17
> +#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
> +#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
> +
> +#define MIPI_CAL_BIAS_PAD_CFG2 0x18
> +#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
> +#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
> +#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
> +
> +struct tegra_mipi_pad {
> + unsigned long data;
> + unsigned long clk;
> +};
> +
> +struct tegra_mipi_soc {
> + bool has_clk_lane;
> + const struct tegra_mipi_pad *pads;
> + unsigned int num_pads;
> +
> + bool clock_enable_override;
> + bool needs_vclamp_ref;
> +
> + /* bias pad configuration settings */
> + u8 pad_drive_down_ref;
> + u8 pad_drive_up_ref;
> +
> + u8 pad_vclamp_level;
> + u8 pad_vauxp_level;
> +
> + /* calibration settings for data lanes */
> + u8 hspdos;
> + u8 hspuos;
> + u8 termos;
> +
> + /* calibration settings for clock lanes */
> + u8 hsclkpdos;
> + u8 hsclkpuos;
> +};
> +
> +struct tegra_mipi {
> + const struct tegra_mipi_soc *soc;
> + struct device *dev;
> + void __iomem *regs;
> + struct mutex lock;
> + struct clk *clk;
> +
> + unsigned long usage_count;
> +};
> +
> +static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
> + unsigned long offset)
> +{
> + return readl(mipi->regs + (offset << 2));
> +}
> +
> +static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
> + unsigned long offset)
> +{
> + writel(value, mipi->regs + (offset << 2));
> +}
> +
> +static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
> +{
> + u32 value;
> + int err;
> +
> + err = clk_enable(mipi->clk);
> + if (err < 0)
> + return err;
> +
> + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
> + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
> +
> + if (mipi->soc->needs_vclamp_ref)
> + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
> +
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
> +
> + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
> + value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> +
> + clk_disable(mipi->clk);
> +
> + return 0;
> +}
> +
> +static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
> +{
> + u32 value;
> + int err;
> +
> + err = clk_enable(mipi->clk);
> + if (err < 0)
> + return err;
> +
> + /*
> + * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
> + * supplies the DSI pads. This must be kept enabled until none of the
> + * DSI lanes are used anymore.
> + */
> + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
> + value |= MIPI_CAL_BIAS_PAD_PDVREG;
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> +
> + /*
> + * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
> + * control a regulator that supplies current to the pre-driver logic.
> + * Powering down this regulator causes DSI to fail, so it must remain
> + * powered on until none of the DSI lanes are used anymore.
> + */
> + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
> +
> + if (mipi->soc->needs_vclamp_ref)
> + value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
> +
> + value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
> +
> + return 0;
> +}
> +
> +static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
> +{
> + struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
> + int err = 0;
> +
> + mutex_lock(&mipi->lock);
> +
> + if (mipi->usage_count++ == 0)
> + err = tegra114_mipi_power_up(mipi);
> +
> + mutex_unlock(&mipi->lock);
> +
> + return err;
> +}
> +
> +static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
> +{
> + struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
> + int err = 0;
> +
> + mutex_lock(&mipi->lock);
> +
> + if (--mipi->usage_count == 0)
> + err = tegra114_mipi_power_down(mipi);
> +
> + mutex_unlock(&mipi->lock);
> +
> + return err;
> +}
> +
> +static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
> +{
> + struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
> + void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
> + u32 value;
> + int err;
> +
> + err = readl_relaxed_poll_timeout(status_reg, value,
> + !(value & MIPI_CAL_STATUS_ACTIVE) &&
> + (value & MIPI_CAL_STATUS_DONE), 50,
> + 250000);
> + mutex_unlock(&mipi->lock);
> + clk_disable(mipi->clk);
> +
> + return err;
> +}
> +
> +static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
> +{
> + struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
> + const struct tegra_mipi_soc *soc = mipi->soc;
> + unsigned int i;
> + u32 value;
> + int err;
> +
> + err = clk_enable(mipi->clk);
> + if (err < 0)
> + return err;
> +
> + mutex_lock(&mipi->lock);
> +
> + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
> + MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
> +
> + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
> + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
> + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
> + value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
> + value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
> + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
> +
> + for (i = 0; i < soc->num_pads; i++) {
> + u32 clk = 0, data = 0;
> +
> + if (mipidev->pads & BIT(i)) {
> + data = MIPI_CAL_CONFIG_SELECT |
> + MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
> + MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
> + MIPI_CAL_CONFIG_TERMOS(soc->termos);
> + clk = MIPI_CAL_CONFIG_SELECT |
> + MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
> + MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
> + }
> +
> + tegra_mipi_writel(mipi, data, soc->pads[i].data);
> +
> + if (soc->has_clk_lane && soc->pads[i].clk != 0)
> + tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
> + }
> +
> + value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
> + value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
> + value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
> + value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
> + value |= MIPI_CAL_CTRL_PRESCALE(0x2);
> +
> + if (!soc->clock_enable_override)
> + value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
> + else
> + value |= MIPI_CAL_CTRL_CLKEN_OVR;
> +
> + tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
> +
> + /* clear any pending status bits */
> + value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
> + tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
> +
> + value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
> + value |= MIPI_CAL_CTRL_START;
> + tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
> +
> + /*
> + * Wait for min 72uS to let calibration logic finish calibration
> + * sequence codes before waiting for pads idle state to apply the
> + * results.
> + */
> + usleep_range(75, 80);
> +
> + return 0;
> +}
> +
> +static const struct tegra_mipi_ops tegra114_mipi_ops = {
> + .enable = tegra114_mipi_enable,
> + .disable = tegra114_mipi_disable,
> + .start_calibration = tegra114_mipi_start_calibration,
> + .finish_calibration = tegra114_mipi_finish_calibration,
> +};
> +
> +static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
> + { .data = MIPI_CAL_CONFIG_CSIA },
> + { .data = MIPI_CAL_CONFIG_CSIB },
> + { .data = MIPI_CAL_CONFIG_CSIC },
> + { .data = MIPI_CAL_CONFIG_CSID },
> + { .data = MIPI_CAL_CONFIG_CSIE },
> + { .data = MIPI_CAL_CONFIG_DSIA },
> + { .data = MIPI_CAL_CONFIG_DSIB },
> + { .data = MIPI_CAL_CONFIG_DSIC },
> + { .data = MIPI_CAL_CONFIG_DSID },
> +};
> +
> +static const struct tegra_mipi_soc tegra114_mipi_soc = {
> + .has_clk_lane = false,
> + .pads = tegra114_mipi_pads,
> + .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
> + .clock_enable_override = true,
> + .needs_vclamp_ref = true,
> + .pad_drive_down_ref = 0x2,
> + .pad_drive_up_ref = 0x0,
> + .pad_vclamp_level = 0x0,
> + .pad_vauxp_level = 0x0,
> + .hspdos = 0x0,
> + .hspuos = 0x4,
> + .termos = 0x5,
> + .hsclkpdos = 0x0,
> + .hsclkpuos = 0x4,
> +};
> +
> +static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
> + { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
> + { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
> + { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
> + { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
> + { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
> + { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
> + { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
> +};
> +
> +static const struct tegra_mipi_soc tegra124_mipi_soc = {
> + .has_clk_lane = true,
> + .pads = tegra124_mipi_pads,
> + .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
> + .clock_enable_override = true,
> + .needs_vclamp_ref = true,
> + .pad_drive_down_ref = 0x2,
> + .pad_drive_up_ref = 0x0,
> + .pad_vclamp_level = 0x0,
> + .pad_vauxp_level = 0x0,
> + .hspdos = 0x0,
> + .hspuos = 0x0,
> + .termos = 0x0,
> + .hsclkpdos = 0x1,
> + .hsclkpuos = 0x2,
> +};
> +
> +static const struct tegra_mipi_soc tegra132_mipi_soc = {
> + .has_clk_lane = true,
> + .pads = tegra124_mipi_pads,
> + .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
> + .clock_enable_override = false,
> + .needs_vclamp_ref = false,
> + .pad_drive_down_ref = 0x0,
> + .pad_drive_up_ref = 0x3,
> + .pad_vclamp_level = 0x0,
> + .pad_vauxp_level = 0x0,
> + .hspdos = 0x0,
> + .hspuos = 0x0,
> + .termos = 0x0,
> + .hsclkpdos = 0x3,
> + .hsclkpuos = 0x2,
> +};
> +
> +static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
> + { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
> + { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
> + { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
> + { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
> + { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
> +};
> +
> +static const struct tegra_mipi_soc tegra210_mipi_soc = {
> + .has_clk_lane = true,
> + .pads = tegra210_mipi_pads,
> + .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
> + .clock_enable_override = true,
> + .needs_vclamp_ref = false,
> + .pad_drive_down_ref = 0x0,
> + .pad_drive_up_ref = 0x3,
> + .pad_vclamp_level = 0x1,
> + .pad_vauxp_level = 0x1,
> + .hspdos = 0x0,
> + .hspuos = 0x2,
> + .termos = 0x0,
> + .hsclkpdos = 0x0,
> + .hsclkpuos = 0x2,
> +};
> +
> +static const struct of_device_id tegra_mipi_of_match[] = {
> + { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
> + { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
> + { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
> + { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
> + { },
> +};
> +
> +static int tegra_mipi_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *match;
> + struct tegra_mipi *mipi;
> +
> + match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
> + if (!match)
> + return -ENODEV;
> +
> + mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
> + if (!mipi)
> + return -ENOMEM;
> +
> + mipi->soc = match->data;
> + mipi->dev = &pdev->dev;
> +
> + mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
> + if (IS_ERR(mipi->regs))
> + return PTR_ERR(mipi->regs);
> +
> + mutex_init(&mipi->lock);
> +
> + mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
> + if (IS_ERR(mipi->clk)) {
> + dev_err(&pdev->dev, "failed to get clock\n");
> + return PTR_ERR(mipi->clk);
> + }
> +
> + platform_set_drvdata(pdev, mipi);
> +
> + return tegra_mipi_add_provider(pdev->dev.of_node,
> + &tegra114_mipi_ops);
> +}
> +
> +struct platform_driver tegra_mipi_driver = {
> + .driver = {
> + .name = "tegra-mipi",
> + .of_match_table = tegra_mipi_of_match,
> + },
> + .probe = tegra_mipi_probe,
> +};
> diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> index 74c92db1032f..9e3bd6109781 100644
> --- a/drivers/staging/media/tegra-video/csi.c
> +++ b/drivers/staging/media/tegra-video/csi.c
> @@ -12,6 +12,7 @@
> #include <linux/of_graph.h>
> #include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> +#include <linux/tegra-mipi-cal.h>
>
> #include <media/v4l2-fwnode.h>
>
> diff --git a/include/linux/host1x.h b/include/linux/host1x.h
> index 9fa9c30a34e6..b1c6514859d3 100644
> --- a/include/linux/host1x.h
> +++ b/include/linux/host1x.h
> @@ -453,16 +453,6 @@ void host1x_client_unregister(struct host1x_client *client);
> int host1x_client_suspend(struct host1x_client *client);
> int host1x_client_resume(struct host1x_client *client);
>
> -struct tegra_mipi_device;
> -
> -struct tegra_mipi_device *tegra_mipi_request(struct device *device,
> - struct device_node *np);
> -void tegra_mipi_free(struct tegra_mipi_device *device);
> -int tegra_mipi_enable(struct tegra_mipi_device *device);
> -int tegra_mipi_disable(struct tegra_mipi_device *device);
> -int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
> -int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
> -
> /* host1x memory contexts */
>
> struct host1x_memory_context {
> diff --git a/include/linux/tegra-mipi-cal.h b/include/linux/tegra-mipi-cal.h
> new file mode 100644
> index 000000000000..77342ee7674f
> --- /dev/null
> +++ b/include/linux/tegra-mipi-cal.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __TEGRA_MIPI_CAL_H_
> +#define __TEGRA_MIPI_CAL_H_
> +
> +struct tegra_mipi_device {
> + const struct tegra_mipi_ops *ops;
> + struct platform_device *pdev;
> + unsigned long pads;
> +};
> +
> +/**
> + * Operations for Tegra MIPI calibration device
> + */
> +struct tegra_mipi_ops {
> + /**
> + * @enable:
> + *
> + * Enable MIPI calibration device
> + */
> + int (*enable)(struct tegra_mipi_device *device);
> +
> + /**
> + * @disable:
> + *
> + * Disable MIPI calibration device
> + */
> + int (*disable)(struct tegra_mipi_device *device);
> +
> + /**
> + * @start_calibration:
> + *
> + * Start MIPI calibration
> + */
> + int (*start_calibration)(struct tegra_mipi_device *device);
> +
> + /**
> + * @finish_calibration:
> + *
> + * Finish MIPI calibration
> + */
> + int (*finish_calibration)(struct tegra_mipi_device *device);
> +};
> +
> +int tegra_mipi_add_provider(struct device_node *np, const struct tegra_mipi_ops *ops);
> +
> +struct tegra_mipi_device *tegra_mipi_request(struct device *device,
> + struct device_node *np);
> +void tegra_mipi_free(struct tegra_mipi_device *device);
> +
> +int tegra_mipi_enable(struct tegra_mipi_device *device);
> +int tegra_mipi_disable(struct tegra_mipi_device *device);
> +int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
> +int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
> +
> +#endif /* __TEGRA_MIPI_CAL_H_ */
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node
2025-09-25 15:16 ` [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node Svyatoslav Ryhel
@ 2025-10-01 4:27 ` Mikko Perttunen
0 siblings, 0 replies; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 4:27 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Svyatoslav Ryhel, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann,
Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> avdd-dsi-csi-supply belongs in CSI node, not VI.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 4 ++--
> arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 4 ++--
> 2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
> index 584461f3a619..4a64fe510f03 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
> @@ -20,10 +20,10 @@ dpaux@54040000 {
> vi@54080000 {
> status = "okay";
>
> - avdd-dsi-csi-supply = <&vdd_dsi_csi>;
> -
> csi@838 {
> status = "okay";
> +
> + avdd-dsi-csi-supply = <&vdd_dsi_csi>;
> };
> };
>
> diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
> index ec0e84cb83ef..f1d2606d9808 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
> +++ b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts
> @@ -64,10 +64,10 @@ dpaux@54040000 {
> vi@54080000 {
> status = "okay";
>
> - avdd-dsi-csi-supply = <&vdd_sys_1v2>;
> -
> csi@838 {
> status = "okay";
> +
> + avdd-dsi-csi-supply = <&vdd_sys_1v2>;
> };
> };
>
>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-09-25 15:16 ` [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations Svyatoslav Ryhel
@ 2025-10-01 4:38 ` Mikko Perttunen
2025-10-01 5:07 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 4:38 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Svyatoslav Ryhel, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann,
Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> Simplify format align calculations by slightly modifying supported formats
> structure. Adjusted U and V offset calculations for planar formats since
> YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> and V) so stride is width * 3/2, but offset must be calculated with plain
> width since each plain has stride width * 1. This aligns with downstream
plane
> behavior which uses same approach for offset calculations.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> drivers/staging/media/tegra-video/vi.h | 3 +-
> 2 files changed, 27 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> index 7c3ff843235d..b7a39723dfc2 100644
> --- a/drivers/staging/media/tegra-video/tegra20.c
> +++ b/drivers/staging/media/tegra-video/tegra20.c
> @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
>
> - switch (pix->pixelformat) {
> - case V4L2_PIX_FMT_UYVY:
> - case V4L2_PIX_FMT_VYUY:
> - case V4L2_PIX_FMT_YUYV:
> - case V4L2_PIX_FMT_YVYU:
> - pix->bytesperline = roundup(pix->width, 2) * 2;
> - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> - break;
> - case V4L2_PIX_FMT_YUV420:
> - case V4L2_PIX_FMT_YVU420:
> - pix->bytesperline = roundup(pix->width, 8);
> - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> - break;
> - }
> + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> + pix->sizeimage = pix->bytesperline * pix->height;
> }
>
> /*
> @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> {
> unsigned int stride = chan->format.bytesperline;
> unsigned int height = chan->format.height;
> + unsigned int width = chan->format.width;
>
> chan->start_offset = 0;
>
> @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
>
> case V4L2_PIX_FMT_YUV420:
> case V4L2_PIX_FMT_YVU420:
> - chan->addr_offset_u = stride * height;
> - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> + chan->addr_offset_u = width * height;
> + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
>
> /* For YVU420, we swap the locations of the U and V planes. */
> if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> chan->start_offset_v = chan->addr_offset_v;
>
> if (chan->vflip) {
> - chan->start_offset += stride * (height - 1);
> - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> + chan->start_offset += width * (height - 1);
> + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> }
> if (chan->hflip) {
> - chan->start_offset += stride - 1;
> - chan->start_offset_u += (stride / 2) - 1;
> - chan->start_offset_v += (stride / 2) - 1;
> + chan->start_offset += width - 1;
> + chan->start_offset_u += (width / 2) - 1;
> + chan->start_offset_v += (width / 2) - 1;
> }
> break;
> }
> @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> .vi_stop_streaming = tegra20_vi_stop_streaming,
> };
>
> -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> -{ \
> - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> - .bpp = BPP, \
> - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> +{ \
> + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> + .bit_width = BIT_WIDTH, \
> + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> + .bpp = BPP, \
> + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> }
>
> static const struct tegra_video_format tegra20_video_formats[] = {
> - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> + /* YUV422 */
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> };
Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
>
> const struct tegra_vi_soc tegra20_vi_soc = {
> diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> index bfadde8858d4..5cbc0606ed6c 100644
> --- a/drivers/staging/media/tegra-video/vi.h
> +++ b/drivers/staging/media/tegra-video/vi.h
> @@ -281,7 +281,8 @@ enum tegra_image_dt {
> * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> * @code: media bus format code
> - * @bpp: bytes per pixel (when stored in memory)
> + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> + * bits per pixel for Tegra20/Tegra30
> * @img_fmt: image format (for CSI/Tegra210 only)
> * @fourcc: V4L2 pixel format FCC identifier
> */
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-09-25 15:16 ` [PATCH v3 22/22] staging: media: tegra-video: add CSI support " Svyatoslav Ryhel
@ 2025-10-01 5:04 ` Mikko Perttunen
2025-10-01 5:15 ` Svyatoslav Ryhel
2025-10-02 17:49 ` Svyatoslav Ryhel
0 siblings, 2 replies; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 5:04 UTC (permalink / raw)
To: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Svyatoslav Ryhel, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann,
Svyatoslav Ryhel
Cc: dri-devel, devicetree, linux-tegra, linux-kernel, linux-media,
linux-clk, linux-gpio, linux-staging
On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> Add support for MIPI CSI device and calibration logic found in Tegra20 and
> Tegra30 SoC.
>
> To get CSI operational, an additional syncpoint was allocated to serve as
> the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> frame start events. That said, the frame capture function was refactored
> to reflect the addition of the CSI syncpoint, and the CSI-specific
> configuration is guarded by the presence of a passed CSI channel structure
> pointer.
>
> The camera capture setup's configuration was reconsidered: the first two
> writes must be done before tegra_channel_set_stream for MIPI calibration
> to work properly; the third write was moved to VIP/CSI-specific functions
> since it must be source-specific; the function was placed after
> tegra_channel_set_stream so the initial sequence is preserved and expanded.
>
> CSI configuration sequences were added based on downstream 3.1 kernel
> sources and adjusted to the existing video-tegra framework. Although
> Tegra20 and Tegra30 have the same set of configurations, they differ by
> the number of clocks used by CSI.
>
> Dropped the software syncpoint counters in favor of reading syncpoints
> directly and passing the incremented value to the polling function. If the
> syncpoint increase fails, the PP is reset. This change should prevent
> possible race conditions.
>
> MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> have no dedicated hardware block for these operations and use CSI. These
> calls are used for both CSI and DSI to work properly, which is why MIPI
> calibration cannot be contained within CSI. The pads passed to the
> calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> DSI-B (4).
>
> Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> drivers/staging/media/tegra-video/csi.c | 19 +
> drivers/staging/media/tegra-video/csi.h | 4 +
> drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> drivers/staging/media/tegra-video/vi.h | 2 -
> drivers/staging/media/tegra-video/video.c | 6 +
> 5 files changed, 592 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> index 7d70478a07aa..92ee4c84a988 100644
> --- a/drivers/staging/media/tegra-video/csi.c
> +++ b/drivers/staging/media/tegra-video/csi.c
> @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
>
> csi->dev = &pdev->dev;
> csi->ops = csi->soc->ops;
> +
> + if (csi->soc->mipi_ops)
> + tegra_mipi_add_provider(pdev->dev.of_node,
> + csi->soc->mipi_ops);
Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
> +
> + mutex_init(&csi->mipi_lock);
> +
> platform_set_drvdata(pdev, csi);
> pm_runtime_enable(&pdev->dev);
>
> @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> pm_runtime_disable(&pdev->dev);
> }
>
> +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> +extern const struct tegra_csi_soc tegra20_csi_soc;
> +#endif
> +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> +extern const struct tegra_csi_soc tegra30_csi_soc;
> +#endif
> #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> extern const struct tegra_csi_soc tegra210_csi_soc;
> #endif
>
> static const struct of_device_id tegra_csi_of_id_table[] = {
> +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> +#endif
> +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> +#endif
> #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> #endif
> diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> index 1550defb115a..422f30655945 100644
> --- a/drivers/staging/media/tegra-video/csi.h
> +++ b/drivers/staging/media/tegra-video/csi.h
> @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> *
> * @ops: csi hardware operations
> + * @mipi_ops: MIPI calibration operations
> * @csi_max_channels: supported max streaming channels
> * @clk_names: csi and cil clock names
> * @num_clks: total clocks count
> @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> */
> struct tegra_csi_soc {
> const struct tegra_csi_ops *ops;
> + const struct tegra_mipi_ops *mipi_ops;
> unsigned int csi_max_channels;
> const char * const *clk_names;
> unsigned int num_clks;
> @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> * @soc: pointer to SoC data structure
> * @ops: csi operations
> + * @mipi_lock: for MIPI calibration operations
> * @csi_chans: list head for CSI channels
> */
> struct tegra_csi {
> @@ -150,6 +153,7 @@ struct tegra_csi {
> struct regulator *vdd;
> const struct tegra_csi_soc *soc;
> const struct tegra_csi_ops *ops;
> + struct mutex mipi_lock;
> struct list_head csi_chans;
> };
>
> diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> index 8c9655ffa886..d99a04fa25af 100644
> --- a/drivers/staging/media/tegra-video/tegra20.c
> +++ b/drivers/staging/media/tegra-video/tegra20.c
> @@ -4,6 +4,9 @@
> *
> * Copyright (C) 2023 SKIDATA GmbH
> * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> + *
> + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> */
>
> /*
> @@ -12,10 +15,15 @@
> */
>
> #include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/clk/tegra.h>
> #include <linux/delay.h>
> #include <linux/host1x.h>
> +#include <linux/iopoll.h>
> #include <linux/kernel.h>
> #include <linux/kthread.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/tegra-mipi-cal.h>
> #include <linux/v4l2-mediabus.h>
>
> #include "vip.h"
> @@ -42,6 +50,9 @@ enum tegra_vi_out {
> #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
>
> +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> +
> #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> #define VI_INPUT_FIELD_DETECT BIT(27)
> #define VI_INPUT_BT656 BIT(25)
> @@ -88,6 +99,8 @@ enum tegra_vi_out {
> #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> /* TEGRA_VI_OUT_2 supported formats */
> +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
>
> #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> @@ -152,8 +165,106 @@ enum tegra_vi_out {
> #define TEGRA_VI_VI_RAISE 0x01ac
> #define VI_VI_RAISE_ON_EDGE BIT(0)
>
> +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> +
> +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> +#define CSI_PP_SINGLE_SHOT BIT(2)
> +#define CSI_PP_NOP 0
> +#define CSI_PP_ENABLE 1
> +#define CSI_PP_DISABLE 2
> +#define CSI_PP_RESET 3
> +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> +#define CSI_A_PHY_CIL_NOP 0x0
> +#define CSI_A_PHY_CIL_ENABLE 0x1
> +#define CSI_A_PHY_CIL_DISABLE 0x2
> +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> +
> /* --------------------------------------------------------------------------
> - * VI
> + * Read and Write helpers
> */
>
> static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> writel(val, chan->vi->iomem + addr);
> }
>
> +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> +{
> + return readl(chan->vi->iomem + addr);
> +}
> +
> +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> +{
> + writel(val, csi_chan->csi->iomem + addr);
> +}
> +
> +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> +{
> + return readl(csi_chan->csi->iomem + addr);
> +}
> +
> +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> +{
> + writel(val, csi->iomem + addr);
> +}
> +
> +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> +{
> + return readl(csi->iomem + addr);
> +}
> +
> +/* --------------------------------------------------------------------------
> + * VI
> + */
> +
> /*
> * Get the main input format (YUV/RGB...) and the YUV variant as values to
> * be written into registers for the current VI input mbus code.
> @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> {
> struct tegra_vi *vi = chan->vi;
> - struct host1x_syncpt *out_sp;
> + struct host1x_syncpt *out_sp, *fs_sp;
>
> out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> if (!out_sp)
> - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
>
> chan->mw_ack_sp[0] = out_sp;
>
> + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> + if (!fs_sp)
> + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> +
> + chan->frame_start_sp[0] = fs_sp;
> +
> return 0;
> }
>
> static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> {
> host1x_syncpt_put(chan->mw_ack_sp[0]);
> + host1x_syncpt_put(chan->frame_start_sp[0]);
> }
>
> static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> }
>
> static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> - struct tegra_channel_buffer *buf)
> + struct tegra_channel_buffer *buf,
> + struct tegra_csi_channel *csi_chan)
> {
> + u32 val;
> int err;
>
> - chan->next_out_sp_idx++;
> -
> tegra20_channel_vi_buffer_setup(chan, buf);
>
> - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> + if (csi_chan) {
> + u32 port = csi_chan->csi_port_nums[0] & 1;
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> +
> + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> + do {
> + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> + } while (err == -ERESTARTSYS);
This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
> +
> + if (err) {
> + if (err != -ERESTARTSYS)
> + dev_err_ratelimited(&chan->video.dev,
> + "frame start syncpt timeout: %d\n", err);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> + goto exit;
> + }
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> + CSI_PP_DISABLE);
> + } else {
> + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> + }
> +
> + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> + do {
> + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> + } while (err == -ERESTARTSYS);
>
> - /* Wait for syncpt counter to reach frame start event threshold */
> - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> if (err) {
> - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> - return err;
> + if (err != -ERESTARTSYS)
> + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> + goto exit;
> }
>
> - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> + if (!csi_chan)
> + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
>
> +exit:
> release_buffer(chan, buf, VB2_BUF_STATE_DONE);
>
> - return 0;
> + return err;
> }
>
> static int tegra20_chan_capture_kthread_start(void *data)
> {
> struct tegra_vi_channel *chan = data;
> struct tegra_channel_buffer *buf;
> + struct v4l2_subdev *csi_subdev = NULL;
> + struct tegra_csi_channel *csi_chan = NULL;
> unsigned int retries = 0;
> int err = 0;
>
> + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> + if (csi_subdev)
> + csi_chan = to_csi_chan(csi_subdev);
> +
> while (1) {
> /*
> * Source is not streaming if error is non-zero.
> @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> list_del_init(&buf->queue);
> spin_unlock(&chan->start_lock);
>
> - err = tegra20_channel_capture_frame(chan, buf);
> + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> if (!err) {
> retries = 0;
> continue;
> @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> data_type == TEGRA_IMAGE_DT_RAW10) ?
> TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> - int main_output_format;
> - int yuv_output_format;
> -
> - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> -
> - /*
> - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> - * for luminance, which is the default and means not to touch
> - * anything.
> - */
> - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> -
> - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> -
> - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
>
> /* Set up frame size */
> tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> struct media_pipeline *pipe = &chan->video.pipe;
> int err;
>
> - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> -
> err = video_device_pipeline_start(&chan->video, pipe);
> if (err)
> goto error_pipeline_start;
>
> - tegra20_camera_capture_setup(chan);
> + /*
> + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> + * for luminance, which is the default and means not to touch
> + * anything.
> + */
> + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> +
> + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
>
> err = tegra_channel_set_stream(chan, true);
> if (err)
> goto error_set_stream;
>
> + tegra20_camera_capture_setup(chan);
> +
> chan->sequence = 0;
>
> chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> .has_h_v_flip = true,
> };
>
> +/* --------------------------------------------------------------------------
> + * MIPI Calibration
> + */
> +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> +{
> + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> + unsigned int port = mipi->pads;
> + u32 value;
> + int ret;
> +
> + guard(mutex)(&csi->mipi_lock);
> +
> + ret = pm_runtime_resume_and_get(csi->dev);
> + if (ret < 0) {
> + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> + return ret;
> + }
> +
> + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> + CSI_CIL_MIPI_CAL_TERMOS(0));
> + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> + CSI_PAD_DRIV_DN_REF(5) |
> + CSI_PAD_DRIV_UP_REF(7) |
> + CSI_PAD_TERM_REF(0));
> +
> + /* CSI B */
> + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> + CSI_CIL_MIPI_CAL_TERMOS(4);
> +
> + if (port == PORT_B)
> + value |= CSI_CIL_MIPI_CAL_SEL_B;
> +
> + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> +
> + /* CSI A */
> + value = CSI_CIL_MIPI_CAL_STARTCAL |
> + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> + CSI_CIL_MIPI_CAL_TERMOS(4);
> +
> + if (port == PORT_A)
> + value |= CSI_CIL_MIPI_CAL_SEL_A;
> +
> + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> +
> + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> +
> + return 0;
> +}
> +
> +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> +{
> + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> + unsigned int port = mipi->pads;
> + u32 value, pp = 0, cil = 0;
> + int ret;
> +
> + /* This part is only for CSI */
> + if (port > PORT_B) {
> + pm_runtime_put(csi->dev);
> +
> + return 0;
> + }
> +
> + guard(mutex)(&csi->mipi_lock);
> +
> + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> + if (ret < 0) {
> + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> + goto exit;
> + }
> +
> + /* clear status */
> + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> + if (ret < 0) {
> + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> + goto exit;
> + }
> +
> + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> + if (pp | cil) {
> + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> + ret = -EINVAL;
> + goto exit;
> + }
> +
> +exit:
> + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> +
> + /* un-select to avoid interference with DSI */
> + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> + CSI_CIL_MIPI_CAL_TERMOS(4));
> +
> + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> + CSI_CIL_MIPI_CAL_TERMOS(4));
> +
> + pm_runtime_put(csi->dev);
> +
> + return ret;
> +}
> +
> +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> + .start_calibration = tegra20_start_pad_calibration,
> + .finish_calibration = tegra20_finish_pad_calibration,
> +};
> +
> +/* --------------------------------------------------------------------------
> + * CSI
> + */
> +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> +{
> + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> +}
> +
> +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> + u8 portno)
> +{
> + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> + int width = vi_chan->format.width;
> + int height = vi_chan->format.height;
> + u32 data_type = vi_chan->fmtinfo->img_dt;
> + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> +
> + unsigned int main_output_format, yuv_output_format;
> + unsigned int port = portno & 1;
> + u32 value;
> +
> + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> +
> + switch (data_type) {
> + case TEGRA_IMAGE_DT_RAW8:
> + case TEGRA_IMAGE_DT_RAW10:
> + output_channel = TEGRA_VI_OUT_2;
> + if (port == PORT_A)
> + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> + else
> + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> + break;
> + }
> +
> + tegra20_csi_capture_clean(csi_chan);
> +
> + /* CSI port cleanup */
> + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> + CSI_PP_EXP_FRAME_HEIGHT(height) |
> + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> + CSI_PP_LINE_TIMEOUT_ENABLE);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> + CSI_PP_OUTPUT_FORMAT_PIXEL |
> + CSI_PP_DATA_TYPE(data_type) |
> + CSI_PP_CRC_CHECK_ENABLE |
> + CSI_PP_WORD_COUNT_HEADER |
> + CSI_PP_DATA_IDENTIFIER_ENABLE |
> + CSI_PP_PACKET_HEADER_SENT |
> + port);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> + (csi_chan->numlanes - 1));
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> + 0x5); /* Clock settle time */
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> +
> + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> + CSI_PP_DISABLE);
> +
> + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> + yuv_output_format | main_output_format);
> +
> + return 0;
> +};
> +
> +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> +{
> + struct tegra_csi *csi = csi_chan->csi;
> + unsigned int port = portno & 1;
> + u32 value;
> +
> + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> +
> + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> +
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> + CSI_PP_DISABLE);
> +
> + if (csi_chan->numlanes == 4) {
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> + } else {
> + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> + }
> +}
> +
> +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> +{
> + u8 *portnos = csi_chan->csi_port_nums;
> + int ret, i;
> +
> + for (i = 0; i < csi_chan->numgangports; i++) {
> + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> + if (ret)
> + goto stream_start_fail;
> + }
> +
> + return 0;
> +
> +stream_start_fail:
> + for (i = i - 1; i >= 0; i--)
> + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> +
> + return ret;
> +}
> +
> +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> +{
> + u8 *portnos = csi_chan->csi_port_nums;
> + int i;
> +
> + for (i = 0; i < csi_chan->numgangports; i++)
> + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> +}
> +
> +static const struct tegra_csi_ops tegra20_csi_ops = {
> + .csi_start_streaming = tegra20_csi_start_streaming,
> + .csi_stop_streaming = tegra20_csi_stop_streaming,
> +};
> +
> +static const char * const tegra20_csi_clks[] = {
> + NULL,
> +};
> +
> +const struct tegra_csi_soc tegra20_csi_soc = {
> + .ops = &tegra20_csi_ops,
> + .mipi_ops = &tegra20_mipi_ops,
> + .csi_max_channels = 2, /* CSI-A and CSI-B */
> + .clk_names = tegra20_csi_clks,
> + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> +};
> +
> +static const char * const tegra30_csi_clks[] = {
> + "csi",
> + "csia-pad",
> + "csib-pad",
> +};
> +
> +const struct tegra_csi_soc tegra30_csi_soc = {
> + .ops = &tegra20_csi_ops,
> + .mipi_ops = &tegra20_mipi_ops,
> + .csi_max_channels = 2, /* CSI-A and CSI-B */
> + .clk_names = tegra30_csi_clks,
> + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> +};
> +
> /* --------------------------------------------------------------------------
> * VIP
> */
> @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> data_type == TEGRA_IMAGE_DT_RAW10) ?
> TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> - unsigned int main_input_format;
> - unsigned int yuv_input_format;
> + unsigned int main_input_format, yuv_input_format;
> + unsigned int main_output_format, yuv_output_format;
>
> tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
>
> tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
>
> @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
>
> tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
>
> + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> + yuv_output_format | main_output_format);
> +
> return 0;
> }
>
> diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> index 5cbc0606ed6c..bad55e0bd313 100644
> --- a/drivers/staging/media/tegra-video/vi.h
> +++ b/drivers/staging/media/tegra-video/vi.h
> @@ -125,7 +125,6 @@ struct tegra_vi {
> * frame through host1x syncpoint counters (On Tegra20 used for the
> * OUT_1 syncpt)
> * @sp_incr_lock: protects cpu syncpoint increment.
> - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> *
> * @kthread_start_capture: kthread to start capture of single frame when
> * vb buffer is available. This thread programs VI CSI hardware
> @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> /* protects the cpu syncpoint increment */
> spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> - u32 next_out_sp_idx;
>
> struct task_struct *kthread_start_capture;
> wait_queue_head_t start_wait;
> diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> index 6fe8d5301b9c..9f2bddc460bf 100644
> --- a/drivers/staging/media/tegra-video/video.c
> +++ b/drivers/staging/media/tegra-video/video.c
> @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> { .compatible = "nvidia,tegra20-vip", },
> { .compatible = "nvidia,tegra20-vi", },
> #endif
> +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> + { .compatible = "nvidia,tegra20-csi", },
> +#endif
> +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> + { .compatible = "nvidia,tegra30-csi", },
> +#endif
> #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> { .compatible = "nvidia,tegra210-csi", },
> { .compatible = "nvidia,tegra210-vi", },
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-01 4:38 ` Mikko Perttunen
@ 2025-10-01 5:07 ` Svyatoslav Ryhel
2025-10-01 5:35 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-01 5:07 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > Simplify format align calculations by slightly modifying supported formats
> > structure. Adjusted U and V offset calculations for planar formats since
> > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > and V) so stride is width * 3/2, but offset must be calculated with plain
> > width since each plain has stride width * 1. This aligns with downstream
>
> plane
>
> > behavior which uses same approach for offset calculations.
> >
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > drivers/staging/media/tegra-video/vi.h | 3 +-
> > 2 files changed, 27 insertions(+), 34 deletions(-)
> >
> > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > index 7c3ff843235d..b7a39723dfc2 100644
> > --- a/drivers/staging/media/tegra-video/tegra20.c
> > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> >
> > - switch (pix->pixelformat) {
> > - case V4L2_PIX_FMT_UYVY:
> > - case V4L2_PIX_FMT_VYUY:
> > - case V4L2_PIX_FMT_YUYV:
> > - case V4L2_PIX_FMT_YVYU:
> > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > - break;
> > - case V4L2_PIX_FMT_YUV420:
> > - case V4L2_PIX_FMT_YVU420:
> > - pix->bytesperline = roundup(pix->width, 8);
> > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > - break;
> > - }
> > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > + pix->sizeimage = pix->bytesperline * pix->height;
> > }
> >
> > /*
> > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > {
> > unsigned int stride = chan->format.bytesperline;
> > unsigned int height = chan->format.height;
> > + unsigned int width = chan->format.width;
> >
> > chan->start_offset = 0;
> >
> > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> >
> > case V4L2_PIX_FMT_YUV420:
> > case V4L2_PIX_FMT_YVU420:
> > - chan->addr_offset_u = stride * height;
> > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > + chan->addr_offset_u = width * height;
> > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> >
> > /* For YVU420, we swap the locations of the U and V planes. */
> > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > chan->start_offset_v = chan->addr_offset_v;
> >
> > if (chan->vflip) {
> > - chan->start_offset += stride * (height - 1);
> > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > + chan->start_offset += width * (height - 1);
> > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > }
> > if (chan->hflip) {
> > - chan->start_offset += stride - 1;
> > - chan->start_offset_u += (stride / 2) - 1;
> > - chan->start_offset_v += (stride / 2) - 1;
> > + chan->start_offset += width - 1;
> > + chan->start_offset_u += (width / 2) - 1;
> > + chan->start_offset_v += (width / 2) - 1;
> > }
> > break;
> > }
> > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > };
> >
> > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > -{ \
> > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > - .bpp = BPP, \
> > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > +{ \
> > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > + .bit_width = BIT_WIDTH, \
> > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > + .bpp = BPP, \
> > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > }
> >
> > static const struct tegra_video_format tegra20_video_formats[] = {
> > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > + /* YUV422 */
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > };
>
> Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
>
> Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
>
No, this code is actually cleaner and in sync with what downstream
does, Tegra210 bytes per pixel is confusing since it totally neglects
formats with fractional bytes per pixel, it is impossible to set there
3/2 for example, which is used by YUV420.
According to downstream code bytes_per_line =
soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
calculation (12 bits). Meanwhile for planar formats Tegra has 3
different buffers so with offset calculation plain width must be used
(which matches downstream).
> >
> > const struct tegra_vi_soc tegra20_vi_soc = {
> > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > index bfadde8858d4..5cbc0606ed6c 100644
> > --- a/drivers/staging/media/tegra-video/vi.h
> > +++ b/drivers/staging/media/tegra-video/vi.h
> > @@ -281,7 +281,8 @@ enum tegra_image_dt {
> > * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> > * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> > * @code: media bus format code
> > - * @bpp: bytes per pixel (when stored in memory)
> > + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> > + * bits per pixel for Tegra20/Tegra30
> > * @img_fmt: image format (for CSI/Tegra210 only)
> > * @fourcc: V4L2 pixel format FCC identifier
> > */
> >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-10-01 5:04 ` Mikko Perttunen
@ 2025-10-01 5:15 ` Svyatoslav Ryhel
2025-10-01 6:38 ` Mikko Perttunen
2025-10-02 17:49 ` Svyatoslav Ryhel
1 sibling, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-01 5:15 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 08:04 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > Add support for MIPI CSI device and calibration logic found in Tegra20 and
> > Tegra30 SoC.
> >
> > To get CSI operational, an additional syncpoint was allocated to serve as
> > the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> > frame start events. That said, the frame capture function was refactored
> > to reflect the addition of the CSI syncpoint, and the CSI-specific
> > configuration is guarded by the presence of a passed CSI channel structure
> > pointer.
> >
> > The camera capture setup's configuration was reconsidered: the first two
> > writes must be done before tegra_channel_set_stream for MIPI calibration
> > to work properly; the third write was moved to VIP/CSI-specific functions
> > since it must be source-specific; the function was placed after
> > tegra_channel_set_stream so the initial sequence is preserved and expanded.
> >
> > CSI configuration sequences were added based on downstream 3.1 kernel
> > sources and adjusted to the existing video-tegra framework. Although
> > Tegra20 and Tegra30 have the same set of configurations, they differ by
> > the number of clocks used by CSI.
> >
> > Dropped the software syncpoint counters in favor of reading syncpoints
> > directly and passing the incremented value to the polling function. If the
> > syncpoint increase fails, the PP is reset. This change should prevent
> > possible race conditions.
> >
> > MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> > have no dedicated hardware block for these operations and use CSI. These
> > calls are used for both CSI and DSI to work properly, which is why MIPI
> > calibration cannot be contained within CSI. The pads passed to the
> > calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> > DSI-B (4).
> >
> > Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> > drivers/staging/media/tegra-video/csi.c | 19 +
> > drivers/staging/media/tegra-video/csi.h | 4 +
> > drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> > drivers/staging/media/tegra-video/vi.h | 2 -
> > drivers/staging/media/tegra-video/video.c | 6 +
> > 5 files changed, 592 insertions(+), 47 deletions(-)
> >
> > diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> > index 7d70478a07aa..92ee4c84a988 100644
> > --- a/drivers/staging/media/tegra-video/csi.c
> > +++ b/drivers/staging/media/tegra-video/csi.c
> > @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
> >
> > csi->dev = &pdev->dev;
> > csi->ops = csi->soc->ops;
> > +
> > + if (csi->soc->mipi_ops)
> > + tegra_mipi_add_provider(pdev->dev.of_node,
> > + csi->soc->mipi_ops);
>
> Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
>
Your words tegra_mipi_remove_provider are unclear, should I add it and
where should I use it exactly? I can make devm version of
tegra_mipi_remove_provider by adding action there.
> > +
> > + mutex_init(&csi->mipi_lock);
> > +
> > platform_set_drvdata(pdev, csi);
> > pm_runtime_enable(&pdev->dev);
> >
> > @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> > pm_runtime_disable(&pdev->dev);
> > }
> >
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > +extern const struct tegra_csi_soc tegra20_csi_soc;
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > +extern const struct tegra_csi_soc tegra30_csi_soc;
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > extern const struct tegra_csi_soc tegra210_csi_soc;
> > #endif
> >
> > static const struct of_device_id tegra_csi_of_id_table[] = {
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> > #endif
> > diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> > index 1550defb115a..422f30655945 100644
> > --- a/drivers/staging/media/tegra-video/csi.h
> > +++ b/drivers/staging/media/tegra-video/csi.h
> > @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> > * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> > *
> > * @ops: csi hardware operations
> > + * @mipi_ops: MIPI calibration operations
> > * @csi_max_channels: supported max streaming channels
> > * @clk_names: csi and cil clock names
> > * @num_clks: total clocks count
> > @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> > */
> > struct tegra_csi_soc {
> > const struct tegra_csi_ops *ops;
> > + const struct tegra_mipi_ops *mipi_ops;
> > unsigned int csi_max_channels;
> > const char * const *clk_names;
> > unsigned int num_clks;
> > @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> > * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> > * @soc: pointer to SoC data structure
> > * @ops: csi operations
> > + * @mipi_lock: for MIPI calibration operations
> > * @csi_chans: list head for CSI channels
> > */
> > struct tegra_csi {
> > @@ -150,6 +153,7 @@ struct tegra_csi {
> > struct regulator *vdd;
> > const struct tegra_csi_soc *soc;
> > const struct tegra_csi_ops *ops;
> > + struct mutex mipi_lock;
> > struct list_head csi_chans;
> > };
> >
> > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > index 8c9655ffa886..d99a04fa25af 100644
> > --- a/drivers/staging/media/tegra-video/tegra20.c
> > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > @@ -4,6 +4,9 @@
> > *
> > * Copyright (C) 2023 SKIDATA GmbH
> > * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > + *
> > + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> > + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > */
> >
> > /*
> > @@ -12,10 +15,15 @@
> > */
> >
> > #include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/clk/tegra.h>
> > #include <linux/delay.h>
> > #include <linux/host1x.h>
> > +#include <linux/iopoll.h>
> > #include <linux/kernel.h>
> > #include <linux/kthread.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/tegra-mipi-cal.h>
> > #include <linux/v4l2-mediabus.h>
> >
> > #include "vip.h"
> > @@ -42,6 +50,9 @@ enum tegra_vi_out {
> > #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> > #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
> >
> > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> > +
> > #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> > #define VI_INPUT_FIELD_DETECT BIT(27)
> > #define VI_INPUT_BT656 BIT(25)
> > @@ -88,6 +99,8 @@ enum tegra_vi_out {
> > #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > /* TEGRA_VI_OUT_2 supported formats */
> > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> >
> > #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> > @@ -152,8 +165,106 @@ enum tegra_vi_out {
> > #define TEGRA_VI_VI_RAISE 0x01ac
> > #define VI_VI_RAISE_ON_EDGE BIT(0)
> >
> > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> > +
> > +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> > +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> > +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> > +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> > +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> > +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> > +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> > +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> > +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> > +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> > +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> > +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> > +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> > +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> > +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> > +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> > +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> > +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> > +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> > +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> > +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> > +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> > +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> > +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> > +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> > +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> > +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> > +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> > +#define CSI_PP_SINGLE_SHOT BIT(2)
> > +#define CSI_PP_NOP 0
> > +#define CSI_PP_ENABLE 1
> > +#define CSI_PP_DISABLE 2
> > +#define CSI_PP_RESET 3
> > +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> > +#define CSI_A_PHY_CIL_NOP 0x0
> > +#define CSI_A_PHY_CIL_ENABLE 0x1
> > +#define CSI_A_PHY_CIL_DISABLE 0x2
> > +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> > +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> > +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> > +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> > +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> > +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> > +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> > +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> > +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> > +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> > +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> > +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> > +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> > +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> > +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> > +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> > +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> > +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> > +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> > +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> > +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> > +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> > +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> > +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> > +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> > +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> > +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> > +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> > +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> > +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> > +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> > +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> > +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> > +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> > +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> > +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> > +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> > +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> > +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> > +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> > +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> > +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> > +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> > +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> > +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> > +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> > +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> > +
> > /* --------------------------------------------------------------------------
> > - * VI
> > + * Read and Write helpers
> > */
> >
> > static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> > @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> > writel(val, chan->vi->iomem + addr);
> > }
> >
> > +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> > +{
> > + return readl(chan->vi->iomem + addr);
> > +}
> > +
> > +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> > +{
> > + writel(val, csi_chan->csi->iomem + addr);
> > +}
> > +
> > +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> > +{
> > + return readl(csi_chan->csi->iomem + addr);
> > +}
> > +
> > +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> > +{
> > + writel(val, csi->iomem + addr);
> > +}
> > +
> > +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> > +{
> > + return readl(csi->iomem + addr);
> > +}
> > +
> > +/* --------------------------------------------------------------------------
> > + * VI
> > + */
> > +
> > /*
> > * Get the main input format (YUV/RGB...) and the YUV variant as values to
> > * be written into registers for the current VI input mbus code.
> > @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> > static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> > {
> > struct tegra_vi *vi = chan->vi;
> > - struct host1x_syncpt *out_sp;
> > + struct host1x_syncpt *out_sp, *fs_sp;
> >
> > out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > if (!out_sp)
> > - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> > + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
> >
> > chan->mw_ack_sp[0] = out_sp;
> >
> > + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > + if (!fs_sp)
> > + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> > +
> > + chan->frame_start_sp[0] = fs_sp;
> > +
> > return 0;
> > }
> >
> > static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> > {
> > host1x_syncpt_put(chan->mw_ack_sp[0]);
> > + host1x_syncpt_put(chan->frame_start_sp[0]);
> > }
> >
> > static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> > }
> >
> > static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> > - struct tegra_channel_buffer *buf)
> > + struct tegra_channel_buffer *buf,
> > + struct tegra_csi_channel *csi_chan)
> > {
> > + u32 val;
> > int err;
> >
> > - chan->next_out_sp_idx++;
> > -
> > tegra20_channel_vi_buffer_setup(chan, buf);
> >
> > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > + if (csi_chan) {
> > + u32 port = csi_chan->csi_port_nums[0] & 1;
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> > +
> > + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> > + do {
> > + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> > + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > + } while (err == -ERESTARTSYS);
>
> This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
>
This is caused by dma_fence_wait_timeout being unconditionally
interruptible. I do not want to touch host1x stuff, this patchset
already takes too much resources.
> > +
> > + if (err) {
> > + if (err != -ERESTARTSYS)
> > + dev_err_ratelimited(&chan->video.dev,
> > + "frame start syncpt timeout: %d\n", err);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> > + goto exit;
> > + }
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > + } else {
> > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > + }
> > +
> > + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > + do {
> > + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> > + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > + } while (err == -ERESTARTSYS);
> >
> > - /* Wait for syncpt counter to reach frame start event threshold */
> > - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> > - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > if (err) {
> > - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> > - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> > - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> > - return err;
> > + if (err != -ERESTARTSYS)
> > + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> > + goto exit;
> > }
> >
> > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > + if (!csi_chan)
> > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> >
> > +exit:
> > release_buffer(chan, buf, VB2_BUF_STATE_DONE);
> >
> > - return 0;
> > + return err;
> > }
> >
> > static int tegra20_chan_capture_kthread_start(void *data)
> > {
> > struct tegra_vi_channel *chan = data;
> > struct tegra_channel_buffer *buf;
> > + struct v4l2_subdev *csi_subdev = NULL;
> > + struct tegra_csi_channel *csi_chan = NULL;
> > unsigned int retries = 0;
> > int err = 0;
> >
> > + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> > + if (csi_subdev)
> > + csi_chan = to_csi_chan(csi_subdev);
> > +
> > while (1) {
> > /*
> > * Source is not streaming if error is non-zero.
> > @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> > list_del_init(&buf->queue);
> > spin_unlock(&chan->start_lock);
> >
> > - err = tegra20_channel_capture_frame(chan, buf);
> > + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> > if (!err) {
> > retries = 0;
> > continue;
> > @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > - int main_output_format;
> > - int yuv_output_format;
> > -
> > - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> > -
> > - /*
> > - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > - * for luminance, which is the default and means not to touch
> > - * anything.
> > - */
> > - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > -
> > - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > -
> > - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> > - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
> >
> > /* Set up frame size */
> > tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> > @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> > struct media_pipeline *pipe = &chan->video.pipe;
> > int err;
> >
> > - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > -
> > err = video_device_pipeline_start(&chan->video, pipe);
> > if (err)
> > goto error_pipeline_start;
> >
> > - tegra20_camera_capture_setup(chan);
> > + /*
> > + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > + * for luminance, which is the default and means not to touch
> > + * anything.
> > + */
> > + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > +
> > + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> >
> > err = tegra_channel_set_stream(chan, true);
> > if (err)
> > goto error_set_stream;
> >
> > + tegra20_camera_capture_setup(chan);
> > +
> > chan->sequence = 0;
> >
> > chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> > @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> > .has_h_v_flip = true,
> > };
> >
> > +/* --------------------------------------------------------------------------
> > + * MIPI Calibration
> > + */
> > +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> > +{
> > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > + unsigned int port = mipi->pads;
> > + u32 value;
> > + int ret;
> > +
> > + guard(mutex)(&csi->mipi_lock);
> > +
> > + ret = pm_runtime_resume_and_get(csi->dev);
> > + if (ret < 0) {
> > + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> > + CSI_CIL_MIPI_CAL_TERMOS(0));
> > + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> > + CSI_PAD_DRIV_DN_REF(5) |
> > + CSI_PAD_DRIV_UP_REF(7) |
> > + CSI_PAD_TERM_REF(0));
> > +
> > + /* CSI B */
> > + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > +
> > + if (port == PORT_B)
> > + value |= CSI_CIL_MIPI_CAL_SEL_B;
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> > +
> > + /* CSI A */
> > + value = CSI_CIL_MIPI_CAL_STARTCAL |
> > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > +
> > + if (port == PORT_A)
> > + value |= CSI_CIL_MIPI_CAL_SEL_A;
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> > +{
> > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> > + unsigned int port = mipi->pads;
> > + u32 value, pp = 0, cil = 0;
> > + int ret;
> > +
> > + /* This part is only for CSI */
> > + if (port > PORT_B) {
> > + pm_runtime_put(csi->dev);
> > +
> > + return 0;
> > + }
> > +
> > + guard(mutex)(&csi->mipi_lock);
> > +
> > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> > + if (ret < 0) {
> > + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> > + goto exit;
> > + }
> > +
> > + /* clear status */
> > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> > + if (ret < 0) {
> > + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> > + goto exit;
> > + }
> > +
> > + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> > + if (pp | cil) {
> > + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> > + ret = -EINVAL;
> > + goto exit;
> > + }
> > +
> > +exit:
> > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> > +
> > + /* un-select to avoid interference with DSI */
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > +
> > + pm_runtime_put(csi->dev);
> > +
> > + return ret;
> > +}
> > +
> > +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> > + .start_calibration = tegra20_start_pad_calibration,
> > + .finish_calibration = tegra20_finish_pad_calibration,
> > +};
> > +
> > +/* --------------------------------------------------------------------------
> > + * CSI
> > + */
> > +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> > +{
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> > +}
> > +
> > +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> > + u8 portno)
> > +{
> > + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> > + int width = vi_chan->format.width;
> > + int height = vi_chan->format.height;
> > + u32 data_type = vi_chan->fmtinfo->img_dt;
> > + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> > + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> > +
> > + unsigned int main_output_format, yuv_output_format;
> > + unsigned int port = portno & 1;
> > + u32 value;
> > +
> > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > +
> > + switch (data_type) {
> > + case TEGRA_IMAGE_DT_RAW8:
> > + case TEGRA_IMAGE_DT_RAW10:
> > + output_channel = TEGRA_VI_OUT_2;
> > + if (port == PORT_A)
> > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> > + else
> > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> > + break;
> > + }
> > +
> > + tegra20_csi_capture_clean(csi_chan);
> > +
> > + /* CSI port cleanup */
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> > + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> > + CSI_PP_EXP_FRAME_HEIGHT(height) |
> > + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> > + CSI_PP_LINE_TIMEOUT_ENABLE);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> > + CSI_PP_OUTPUT_FORMAT_PIXEL |
> > + CSI_PP_DATA_TYPE(data_type) |
> > + CSI_PP_CRC_CHECK_ENABLE |
> > + CSI_PP_WORD_COUNT_HEADER |
> > + CSI_PP_DATA_IDENTIFIER_ENABLE |
> > + CSI_PP_PACKET_HEADER_SENT |
> > + port);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> > + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> > + (csi_chan->numlanes - 1));
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> > + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> > + 0x5); /* Clock settle time */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > +
> > + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> > + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > + yuv_output_format | main_output_format);
> > +
> > + return 0;
> > +};
> > +
> > +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> > +{
> > + struct tegra_csi *csi = csi_chan->csi;
> > + unsigned int port = portno & 1;
> > + u32 value;
> > +
> > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> > +
> > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > +
> > + if (csi_chan->numlanes == 4) {
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> > + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> > + } else {
> > + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> > + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > + }
> > +}
> > +
> > +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> > +{
> > + u8 *portnos = csi_chan->csi_port_nums;
> > + int ret, i;
> > +
> > + for (i = 0; i < csi_chan->numgangports; i++) {
> > + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> > + if (ret)
> > + goto stream_start_fail;
> > + }
> > +
> > + return 0;
> > +
> > +stream_start_fail:
> > + for (i = i - 1; i >= 0; i--)
> > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > +
> > + return ret;
> > +}
> > +
> > +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> > +{
> > + u8 *portnos = csi_chan->csi_port_nums;
> > + int i;
> > +
> > + for (i = 0; i < csi_chan->numgangports; i++)
> > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > +}
> > +
> > +static const struct tegra_csi_ops tegra20_csi_ops = {
> > + .csi_start_streaming = tegra20_csi_start_streaming,
> > + .csi_stop_streaming = tegra20_csi_stop_streaming,
> > +};
> > +
> > +static const char * const tegra20_csi_clks[] = {
> > + NULL,
> > +};
> > +
> > +const struct tegra_csi_soc tegra20_csi_soc = {
> > + .ops = &tegra20_csi_ops,
> > + .mipi_ops = &tegra20_mipi_ops,
> > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > + .clk_names = tegra20_csi_clks,
> > + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> > +};
> > +
> > +static const char * const tegra30_csi_clks[] = {
> > + "csi",
> > + "csia-pad",
> > + "csib-pad",
> > +};
> > +
> > +const struct tegra_csi_soc tegra30_csi_soc = {
> > + .ops = &tegra20_csi_ops,
> > + .mipi_ops = &tegra20_mipi_ops,
> > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > + .clk_names = tegra30_csi_clks,
> > + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> > +};
> > +
> > /* --------------------------------------------------------------------------
> > * VIP
> > */
> > @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > - unsigned int main_input_format;
> > - unsigned int yuv_input_format;
> > + unsigned int main_input_format, yuv_input_format;
> > + unsigned int main_output_format, yuv_output_format;
> >
> > tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> >
> > tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
> >
> > @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> >
> > tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
> >
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > + yuv_output_format | main_output_format);
> > +
> > return 0;
> > }
> >
> > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > index 5cbc0606ed6c..bad55e0bd313 100644
> > --- a/drivers/staging/media/tegra-video/vi.h
> > +++ b/drivers/staging/media/tegra-video/vi.h
> > @@ -125,7 +125,6 @@ struct tegra_vi {
> > * frame through host1x syncpoint counters (On Tegra20 used for the
> > * OUT_1 syncpt)
> > * @sp_incr_lock: protects cpu syncpoint increment.
> > - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> > *
> > * @kthread_start_capture: kthread to start capture of single frame when
> > * vb buffer is available. This thread programs VI CSI hardware
> > @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> > struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> > /* protects the cpu syncpoint increment */
> > spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> > - u32 next_out_sp_idx;
> >
> > struct task_struct *kthread_start_capture;
> > wait_queue_head_t start_wait;
> > diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> > index 6fe8d5301b9c..9f2bddc460bf 100644
> > --- a/drivers/staging/media/tegra-video/video.c
> > +++ b/drivers/staging/media/tegra-video/video.c
> > @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> > { .compatible = "nvidia,tegra20-vip", },
> > { .compatible = "nvidia,tegra20-vi", },
> > #endif
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > + { .compatible = "nvidia,tegra20-csi", },
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > + { .compatible = "nvidia,tegra30-csi", },
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > { .compatible = "nvidia,tegra210-csi", },
> > { .compatible = "nvidia,tegra210-vi", },
> >
>
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-01 5:07 ` Svyatoslav Ryhel
@ 2025-10-01 5:35 ` Svyatoslav Ryhel
2025-10-01 7:51 ` Mikko Perttunen
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-01 5:35 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
>
> ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> >
> > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > Simplify format align calculations by slightly modifying supported formats
> > > structure. Adjusted U and V offset calculations for planar formats since
> > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > width since each plain has stride width * 1. This aligns with downstream
> >
> > plane
> >
> > > behavior which uses same approach for offset calculations.
> > >
> > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > ---
> > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > >
> > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > index 7c3ff843235d..b7a39723dfc2 100644
> > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > >
> > > - switch (pix->pixelformat) {
> > > - case V4L2_PIX_FMT_UYVY:
> > > - case V4L2_PIX_FMT_VYUY:
> > > - case V4L2_PIX_FMT_YUYV:
> > > - case V4L2_PIX_FMT_YVYU:
> > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > - break;
> > > - case V4L2_PIX_FMT_YUV420:
> > > - case V4L2_PIX_FMT_YVU420:
> > > - pix->bytesperline = roundup(pix->width, 8);
> > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > - break;
> > > - }
> > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > }
> > >
> > > /*
> > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > {
> > > unsigned int stride = chan->format.bytesperline;
> > > unsigned int height = chan->format.height;
> > > + unsigned int width = chan->format.width;
> > >
> > > chan->start_offset = 0;
> > >
> > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > >
> > > case V4L2_PIX_FMT_YUV420:
> > > case V4L2_PIX_FMT_YVU420:
> > > - chan->addr_offset_u = stride * height;
> > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > + chan->addr_offset_u = width * height;
> > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > >
> > > /* For YVU420, we swap the locations of the U and V planes. */
> > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > chan->start_offset_v = chan->addr_offset_v;
> > >
> > > if (chan->vflip) {
> > > - chan->start_offset += stride * (height - 1);
> > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > + chan->start_offset += width * (height - 1);
> > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > }
> > > if (chan->hflip) {
> > > - chan->start_offset += stride - 1;
> > > - chan->start_offset_u += (stride / 2) - 1;
> > > - chan->start_offset_v += (stride / 2) - 1;
> > > + chan->start_offset += width - 1;
> > > + chan->start_offset_u += (width / 2) - 1;
> > > + chan->start_offset_v += (width / 2) - 1;
> > > }
> > > break;
> > > }
> > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > };
> > >
> > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > -{ \
> > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > - .bpp = BPP, \
> > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > +{ \
> > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > + .bit_width = BIT_WIDTH, \
> > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > + .bpp = BPP, \
> > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > }
> > >
> > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > + /* YUV422 */
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > };
> >
> > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> >
> > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> >
>
> No, this code is actually cleaner and in sync with what downstream
> does, Tegra210 bytes per pixel is confusing since it totally neglects
> formats with fractional bytes per pixel, it is impossible to set there
> 3/2 for example, which is used by YUV420.
>
> According to downstream code bytes_per_line =
> soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> calculation (12 bits). Meanwhile for planar formats Tegra has 3
> different buffers so with offset calculation plain width must be used
> (which matches downstream).
>
If you mean use of BPP by VI, I can propose removing bytesperline and
sizeimage configuration from VI entirely and leave this to per-SoC
fmt_align function which does this already anyway and guards every
time those values are referred. This way there will be no instances
where "places not being the actual bytes per line" comes true.
> > >
> > > const struct tegra_vi_soc tegra20_vi_soc = {
> > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > index bfadde8858d4..5cbc0606ed6c 100644
> > > --- a/drivers/staging/media/tegra-video/vi.h
> > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > @@ -281,7 +281,8 @@ enum tegra_image_dt {
> > > * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> > > * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> > > * @code: media bus format code
> > > - * @bpp: bytes per pixel (when stored in memory)
> > > + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> > > + * bits per pixel for Tegra20/Tegra30
> > > * @img_fmt: image format (for CSI/Tegra210 only)
> > > * @fourcc: V4L2 pixel format FCC identifier
> > > */
> > >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-10-01 5:15 ` Svyatoslav Ryhel
@ 2025-10-01 6:38 ` Mikko Perttunen
2025-10-01 15:23 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 6:38 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
On Wednesday, October 1, 2025 2:15 PM Svyatoslav Ryhel wrote:
> ср, 1 жовт. 2025 р. о 08:04 Mikko Perttunen <mperttunen@nvidia.com> пише:
> >
> > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > Add support for MIPI CSI device and calibration logic found in Tegra20 and
> > > Tegra30 SoC.
> > >
> > > To get CSI operational, an additional syncpoint was allocated to serve as
> > > the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> > > frame start events. That said, the frame capture function was refactored
> > > to reflect the addition of the CSI syncpoint, and the CSI-specific
> > > configuration is guarded by the presence of a passed CSI channel structure
> > > pointer.
> > >
> > > The camera capture setup's configuration was reconsidered: the first two
> > > writes must be done before tegra_channel_set_stream for MIPI calibration
> > > to work properly; the third write was moved to VIP/CSI-specific functions
> > > since it must be source-specific; the function was placed after
> > > tegra_channel_set_stream so the initial sequence is preserved and expanded.
> > >
> > > CSI configuration sequences were added based on downstream 3.1 kernel
> > > sources and adjusted to the existing video-tegra framework. Although
> > > Tegra20 and Tegra30 have the same set of configurations, they differ by
> > > the number of clocks used by CSI.
> > >
> > > Dropped the software syncpoint counters in favor of reading syncpoints
> > > directly and passing the incremented value to the polling function. If the
> > > syncpoint increase fails, the PP is reset. This change should prevent
> > > possible race conditions.
> > >
> > > MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> > > have no dedicated hardware block for these operations and use CSI. These
> > > calls are used for both CSI and DSI to work properly, which is why MIPI
> > > calibration cannot be contained within CSI. The pads passed to the
> > > calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> > > DSI-B (4).
> > >
> > > Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > ---
> > > drivers/staging/media/tegra-video/csi.c | 19 +
> > > drivers/staging/media/tegra-video/csi.h | 4 +
> > > drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> > > drivers/staging/media/tegra-video/vi.h | 2 -
> > > drivers/staging/media/tegra-video/video.c | 6 +
> > > 5 files changed, 592 insertions(+), 47 deletions(-)
> > >
> > > diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> > > index 7d70478a07aa..92ee4c84a988 100644
> > > --- a/drivers/staging/media/tegra-video/csi.c
> > > +++ b/drivers/staging/media/tegra-video/csi.c
> > > @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
> > >
> > > csi->dev = &pdev->dev;
> > > csi->ops = csi->soc->ops;
> > > +
> > > + if (csi->soc->mipi_ops)
> > > + tegra_mipi_add_provider(pdev->dev.of_node,
> > > + csi->soc->mipi_ops);
> >
> > Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
> >
>
> Your words tegra_mipi_remove_provider are unclear, should I add it and
> where should I use it exactly? I can make devm version of
> tegra_mipi_remove_provider by adding action there.
Yes, devm_tegra_mipi_add_provider would be a good solution. My intent was that we should clean up the provider registration in an error case or when the CSI device is removed.
FWIW, I've spent a little time looking at the refcounting situation, and it doesn't seem like the device refcount guarantees the driver is still bound. Will need to look at this further, but no need to block this series.
>
> > > +
> > > + mutex_init(&csi->mipi_lock);
> > > +
> > > platform_set_drvdata(pdev, csi);
> > > pm_runtime_enable(&pdev->dev);
> > >
> > > @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> > > pm_runtime_disable(&pdev->dev);
> > > }
> > >
> > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > +extern const struct tegra_csi_soc tegra20_csi_soc;
> > > +#endif
> > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > +extern const struct tegra_csi_soc tegra30_csi_soc;
> > > +#endif
> > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > extern const struct tegra_csi_soc tegra210_csi_soc;
> > > #endif
> > >
> > > static const struct of_device_id tegra_csi_of_id_table[] = {
> > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> > > +#endif
> > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> > > +#endif
> > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> > > #endif
> > > diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> > > index 1550defb115a..422f30655945 100644
> > > --- a/drivers/staging/media/tegra-video/csi.h
> > > +++ b/drivers/staging/media/tegra-video/csi.h
> > > @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> > > * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> > > *
> > > * @ops: csi hardware operations
> > > + * @mipi_ops: MIPI calibration operations
> > > * @csi_max_channels: supported max streaming channels
> > > * @clk_names: csi and cil clock names
> > > * @num_clks: total clocks count
> > > @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> > > */
> > > struct tegra_csi_soc {
> > > const struct tegra_csi_ops *ops;
> > > + const struct tegra_mipi_ops *mipi_ops;
> > > unsigned int csi_max_channels;
> > > const char * const *clk_names;
> > > unsigned int num_clks;
> > > @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> > > * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> > > * @soc: pointer to SoC data structure
> > > * @ops: csi operations
> > > + * @mipi_lock: for MIPI calibration operations
> > > * @csi_chans: list head for CSI channels
> > > */
> > > struct tegra_csi {
> > > @@ -150,6 +153,7 @@ struct tegra_csi {
> > > struct regulator *vdd;
> > > const struct tegra_csi_soc *soc;
> > > const struct tegra_csi_ops *ops;
> > > + struct mutex mipi_lock;
> > > struct list_head csi_chans;
> > > };
> > >
> > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > index 8c9655ffa886..d99a04fa25af 100644
> > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > @@ -4,6 +4,9 @@
> > > *
> > > * Copyright (C) 2023 SKIDATA GmbH
> > > * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > + *
> > > + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> > > + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > */
> > >
> > > /*
> > > @@ -12,10 +15,15 @@
> > > */
> > >
> > > #include <linux/bitfield.h>
> > > +#include <linux/clk.h>
> > > +#include <linux/clk/tegra.h>
> > > #include <linux/delay.h>
> > > #include <linux/host1x.h>
> > > +#include <linux/iopoll.h>
> > > #include <linux/kernel.h>
> > > #include <linux/kthread.h>
> > > +#include <linux/pm_runtime.h>
> > > +#include <linux/tegra-mipi-cal.h>
> > > #include <linux/v4l2-mediabus.h>
> > >
> > > #include "vip.h"
> > > @@ -42,6 +50,9 @@ enum tegra_vi_out {
> > > #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> > > #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
> > >
> > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> > > +
> > > #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> > > #define VI_INPUT_FIELD_DETECT BIT(27)
> > > #define VI_INPUT_BT656 BIT(25)
> > > @@ -88,6 +99,8 @@ enum tegra_vi_out {
> > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > /* TEGRA_VI_OUT_2 supported formats */
> > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > >
> > > #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> > > @@ -152,8 +165,106 @@ enum tegra_vi_out {
> > > #define TEGRA_VI_VI_RAISE 0x01ac
> > > #define VI_VI_RAISE_ON_EDGE BIT(0)
> > >
> > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> > > +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> > > +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> > > +
> > > +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> > > +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> > > +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> > > +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> > > +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> > > +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> > > +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> > > +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> > > +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> > > +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> > > +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> > > +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> > > +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> > > +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> > > +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> > > +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> > > +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> > > +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> > > +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> > > +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> > > +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> > > +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> > > +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> > > +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> > > +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> > > +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> > > +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> > > +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> > > +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> > > +#define CSI_PP_SINGLE_SHOT BIT(2)
> > > +#define CSI_PP_NOP 0
> > > +#define CSI_PP_ENABLE 1
> > > +#define CSI_PP_DISABLE 2
> > > +#define CSI_PP_RESET 3
> > > +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> > > +#define CSI_A_PHY_CIL_NOP 0x0
> > > +#define CSI_A_PHY_CIL_ENABLE 0x1
> > > +#define CSI_A_PHY_CIL_DISABLE 0x2
> > > +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> > > +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> > > +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> > > +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> > > +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> > > +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> > > +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> > > +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> > > +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> > > +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> > > +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> > > +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> > > +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> > > +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> > > +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> > > +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> > > +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> > > +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> > > +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> > > +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> > > +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> > > +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> > > +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> > > +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> > > +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> > > +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> > > +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> > > +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> > > +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> > > +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> > > +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> > > +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> > > +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> > > +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> > > +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> > > +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> > > +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> > > +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> > > +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> > > +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> > > +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> > > +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> > > +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> > > +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> > > +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> > > +
> > > /* --------------------------------------------------------------------------
> > > - * VI
> > > + * Read and Write helpers
> > > */
> > >
> > > static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> > > @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> > > writel(val, chan->vi->iomem + addr);
> > > }
> > >
> > > +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> > > +{
> > > + return readl(chan->vi->iomem + addr);
> > > +}
> > > +
> > > +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> > > +{
> > > + writel(val, csi_chan->csi->iomem + addr);
> > > +}
> > > +
> > > +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> > > +{
> > > + return readl(csi_chan->csi->iomem + addr);
> > > +}
> > > +
> > > +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> > > +{
> > > + writel(val, csi->iomem + addr);
> > > +}
> > > +
> > > +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> > > +{
> > > + return readl(csi->iomem + addr);
> > > +}
> > > +
> > > +/* --------------------------------------------------------------------------
> > > + * VI
> > > + */
> > > +
> > > /*
> > > * Get the main input format (YUV/RGB...) and the YUV variant as values to
> > > * be written into registers for the current VI input mbus code.
> > > @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> > > static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> > > {
> > > struct tegra_vi *vi = chan->vi;
> > > - struct host1x_syncpt *out_sp;
> > > + struct host1x_syncpt *out_sp, *fs_sp;
> > >
> > > out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > if (!out_sp)
> > > - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
> > >
> > > chan->mw_ack_sp[0] = out_sp;
> > >
> > > + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > + if (!fs_sp)
> > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> > > +
> > > + chan->frame_start_sp[0] = fs_sp;
> > > +
> > > return 0;
> > > }
> > >
> > > static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> > > {
> > > host1x_syncpt_put(chan->mw_ack_sp[0]);
> > > + host1x_syncpt_put(chan->frame_start_sp[0]);
> > > }
> > >
> > > static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> > > }
> > >
> > > static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> > > - struct tegra_channel_buffer *buf)
> > > + struct tegra_channel_buffer *buf,
> > > + struct tegra_csi_channel *csi_chan)
> > > {
> > > + u32 val;
> > > int err;
> > >
> > > - chan->next_out_sp_idx++;
> > > -
> > > tegra20_channel_vi_buffer_setup(chan, buf);
> > >
> > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > + if (csi_chan) {
> > > + u32 port = csi_chan->csi_port_nums[0] & 1;
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> > > +
> > > + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> > > + do {
> > > + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> > > + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > + } while (err == -ERESTARTSYS);
> >
> > This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
> >
>
> This is caused by dma_fence_wait_timeout being unconditionally
> interruptible. I do not want to touch host1x stuff, this patchset
> already takes too much resources.
Sure, I don't think we need to change host1x. Since this function is only called from non-user context, even if the wait is interruptible it should never be actually interrupted. So I think you can just drop the ERESTARTSYS handling.
>
> > > +
> > > + if (err) {
> > > + if (err != -ERESTARTSYS)
> > > + dev_err_ratelimited(&chan->video.dev,
> > > + "frame start syncpt timeout: %d\n", err);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> > > + goto exit;
> > > + }
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > + CSI_PP_DISABLE);
> > > + } else {
> > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > + }
> > > +
> > > + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > + do {
> > > + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> > > + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > + } while (err == -ERESTARTSYS);
> > >
> > > - /* Wait for syncpt counter to reach frame start event threshold */
> > > - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> > > - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > if (err) {
> > > - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> > > - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> > > - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> > > - return err;
> > > + if (err != -ERESTARTSYS)
> > > + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> > > + goto exit;
> > > }
> > >
> > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > > + if (!csi_chan)
> > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > >
> > > +exit:
> > > release_buffer(chan, buf, VB2_BUF_STATE_DONE);
> > >
> > > - return 0;
> > > + return err;
> > > }
> > >
> > > static int tegra20_chan_capture_kthread_start(void *data)
> > > {
> > > struct tegra_vi_channel *chan = data;
> > > struct tegra_channel_buffer *buf;
> > > + struct v4l2_subdev *csi_subdev = NULL;
> > > + struct tegra_csi_channel *csi_chan = NULL;
> > > unsigned int retries = 0;
> > > int err = 0;
> > >
> > > + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> > > + if (csi_subdev)
> > > + csi_chan = to_csi_chan(csi_subdev);
> > > +
> > > while (1) {
> > > /*
> > > * Source is not streaming if error is non-zero.
> > > @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> > > list_del_init(&buf->queue);
> > > spin_unlock(&chan->start_lock);
> > >
> > > - err = tegra20_channel_capture_frame(chan, buf);
> > > + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> > > if (!err) {
> > > retries = 0;
> > > continue;
> > > @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > - int main_output_format;
> > > - int yuv_output_format;
> > > -
> > > - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> > > -
> > > - /*
> > > - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > - * for luminance, which is the default and means not to touch
> > > - * anything.
> > > - */
> > > - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > -
> > > - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > > -
> > > - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> > > - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
> > >
> > > /* Set up frame size */
> > > tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> > > @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> > > struct media_pipeline *pipe = &chan->video.pipe;
> > > int err;
> > >
> > > - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > -
> > > err = video_device_pipeline_start(&chan->video, pipe);
> > > if (err)
> > > goto error_pipeline_start;
> > >
> > > - tegra20_camera_capture_setup(chan);
> > > + /*
> > > + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > + * for luminance, which is the default and means not to touch
> > > + * anything.
> > > + */
> > > + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > +
> > > + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > >
> > > err = tegra_channel_set_stream(chan, true);
> > > if (err)
> > > goto error_set_stream;
> > >
> > > + tegra20_camera_capture_setup(chan);
> > > +
> > > chan->sequence = 0;
> > >
> > > chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> > > @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> > > .has_h_v_flip = true,
> > > };
> > >
> > > +/* --------------------------------------------------------------------------
> > > + * MIPI Calibration
> > > + */
> > > +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> > > +{
> > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > + unsigned int port = mipi->pads;
> > > + u32 value;
> > > + int ret;
> > > +
> > > + guard(mutex)(&csi->mipi_lock);
> > > +
> > > + ret = pm_runtime_resume_and_get(csi->dev);
> > > + if (ret < 0) {
> > > + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> > > + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> > > + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> > > + CSI_CIL_MIPI_CAL_TERMOS(0));
> > > + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> > > + CSI_PAD_DRIV_DN_REF(5) |
> > > + CSI_PAD_DRIV_UP_REF(7) |
> > > + CSI_PAD_TERM_REF(0));
> > > +
> > > + /* CSI B */
> > > + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > +
> > > + if (port == PORT_B)
> > > + value |= CSI_CIL_MIPI_CAL_SEL_B;
> > > +
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> > > +
> > > + /* CSI A */
> > > + value = CSI_CIL_MIPI_CAL_STARTCAL |
> > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > +
> > > + if (port == PORT_A)
> > > + value |= CSI_CIL_MIPI_CAL_SEL_A;
> > > +
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> > > +
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> > > +{
> > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> > > + unsigned int port = mipi->pads;
> > > + u32 value, pp = 0, cil = 0;
> > > + int ret;
> > > +
> > > + /* This part is only for CSI */
> > > + if (port > PORT_B) {
> > > + pm_runtime_put(csi->dev);
> > > +
> > > + return 0;
> > > + }
> > > +
> > > + guard(mutex)(&csi->mipi_lock);
> > > +
> > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> > > + if (ret < 0) {
> > > + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> > > + goto exit;
> > > + }
> > > +
> > > + /* clear status */
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> > > + if (ret < 0) {
> > > + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> > > + goto exit;
> > > + }
> > > +
> > > + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> > > + if (pp | cil) {
> > > + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> > > + ret = -EINVAL;
> > > + goto exit;
> > > + }
> > > +
> > > +exit:
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> > > +
> > > + /* un-select to avoid interference with DSI */
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > +
> > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > +
> > > + pm_runtime_put(csi->dev);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> > > + .start_calibration = tegra20_start_pad_calibration,
> > > + .finish_calibration = tegra20_finish_pad_calibration,
> > > +};
> > > +
> > > +/* --------------------------------------------------------------------------
> > > + * CSI
> > > + */
> > > +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> > > +{
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> > > +}
> > > +
> > > +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> > > + u8 portno)
> > > +{
> > > + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> > > + int width = vi_chan->format.width;
> > > + int height = vi_chan->format.height;
> > > + u32 data_type = vi_chan->fmtinfo->img_dt;
> > > + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> > > + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> > > +
> > > + unsigned int main_output_format, yuv_output_format;
> > > + unsigned int port = portno & 1;
> > > + u32 value;
> > > +
> > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > > +
> > > + switch (data_type) {
> > > + case TEGRA_IMAGE_DT_RAW8:
> > > + case TEGRA_IMAGE_DT_RAW10:
> > > + output_channel = TEGRA_VI_OUT_2;
> > > + if (port == PORT_A)
> > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> > > + else
> > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> > > + break;
> > > + }
> > > +
> > > + tegra20_csi_capture_clean(csi_chan);
> > > +
> > > + /* CSI port cleanup */
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> > > + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> > > + CSI_PP_EXP_FRAME_HEIGHT(height) |
> > > + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> > > + CSI_PP_LINE_TIMEOUT_ENABLE);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> > > + CSI_PP_OUTPUT_FORMAT_PIXEL |
> > > + CSI_PP_DATA_TYPE(data_type) |
> > > + CSI_PP_CRC_CHECK_ENABLE |
> > > + CSI_PP_WORD_COUNT_HEADER |
> > > + CSI_PP_DATA_IDENTIFIER_ENABLE |
> > > + CSI_PP_PACKET_HEADER_SENT |
> > > + port);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> > > + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> > > + (csi_chan->numlanes - 1));
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> > > + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> > > + 0x5); /* Clock settle time */
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > +
> > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> > > + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > + CSI_PP_DISABLE);
> > > +
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > + yuv_output_format | main_output_format);
> > > +
> > > + return 0;
> > > +};
> > > +
> > > +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> > > +{
> > > + struct tegra_csi *csi = csi_chan->csi;
> > > + unsigned int port = portno & 1;
> > > + u32 value;
> > > +
> > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> > > +
> > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > +
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > + CSI_PP_DISABLE);
> > > +
> > > + if (csi_chan->numlanes == 4) {
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> > > + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> > > + } else {
> > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> > > + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > + }
> > > +}
> > > +
> > > +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> > > +{
> > > + u8 *portnos = csi_chan->csi_port_nums;
> > > + int ret, i;
> > > +
> > > + for (i = 0; i < csi_chan->numgangports; i++) {
> > > + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> > > + if (ret)
> > > + goto stream_start_fail;
> > > + }
> > > +
> > > + return 0;
> > > +
> > > +stream_start_fail:
> > > + for (i = i - 1; i >= 0; i--)
> > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> > > +{
> > > + u8 *portnos = csi_chan->csi_port_nums;
> > > + int i;
> > > +
> > > + for (i = 0; i < csi_chan->numgangports; i++)
> > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > +}
> > > +
> > > +static const struct tegra_csi_ops tegra20_csi_ops = {
> > > + .csi_start_streaming = tegra20_csi_start_streaming,
> > > + .csi_stop_streaming = tegra20_csi_stop_streaming,
> > > +};
> > > +
> > > +static const char * const tegra20_csi_clks[] = {
> > > + NULL,
> > > +};
> > > +
> > > +const struct tegra_csi_soc tegra20_csi_soc = {
> > > + .ops = &tegra20_csi_ops,
> > > + .mipi_ops = &tegra20_mipi_ops,
> > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > + .clk_names = tegra20_csi_clks,
> > > + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> > > +};
> > > +
> > > +static const char * const tegra30_csi_clks[] = {
> > > + "csi",
> > > + "csia-pad",
> > > + "csib-pad",
> > > +};
> > > +
> > > +const struct tegra_csi_soc tegra30_csi_soc = {
> > > + .ops = &tegra20_csi_ops,
> > > + .mipi_ops = &tegra20_mipi_ops,
> > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > + .clk_names = tegra30_csi_clks,
> > > + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> > > +};
> > > +
> > > /* --------------------------------------------------------------------------
> > > * VIP
> > > */
> > > @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > - unsigned int main_input_format;
> > > - unsigned int yuv_input_format;
> > > + unsigned int main_input_format, yuv_input_format;
> > > + unsigned int main_output_format, yuv_output_format;
> > >
> > > tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > >
> > > tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
> > >
> > > @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > >
> > > tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
> > >
> > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > + yuv_output_format | main_output_format);
> > > +
> > > return 0;
> > > }
> > >
> > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > index 5cbc0606ed6c..bad55e0bd313 100644
> > > --- a/drivers/staging/media/tegra-video/vi.h
> > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > @@ -125,7 +125,6 @@ struct tegra_vi {
> > > * frame through host1x syncpoint counters (On Tegra20 used for the
> > > * OUT_1 syncpt)
> > > * @sp_incr_lock: protects cpu syncpoint increment.
> > > - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> > > *
> > > * @kthread_start_capture: kthread to start capture of single frame when
> > > * vb buffer is available. This thread programs VI CSI hardware
> > > @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> > > struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> > > /* protects the cpu syncpoint increment */
> > > spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> > > - u32 next_out_sp_idx;
> > >
> > > struct task_struct *kthread_start_capture;
> > > wait_queue_head_t start_wait;
> > > diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> > > index 6fe8d5301b9c..9f2bddc460bf 100644
> > > --- a/drivers/staging/media/tegra-video/video.c
> > > +++ b/drivers/staging/media/tegra-video/video.c
> > > @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> > > { .compatible = "nvidia,tegra20-vip", },
> > > { .compatible = "nvidia,tegra20-vi", },
> > > #endif
> > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > + { .compatible = "nvidia,tegra20-csi", },
> > > +#endif
> > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > + { .compatible = "nvidia,tegra30-csi", },
> > > +#endif
> > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > { .compatible = "nvidia,tegra210-csi", },
> > > { .compatible = "nvidia,tegra210-vi", },
> > >
> >
> >
> >
> >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-01 5:35 ` Svyatoslav Ryhel
@ 2025-10-01 7:51 ` Mikko Perttunen
2025-10-01 7:59 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-01 7:51 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> >
> > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > >
> > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > Simplify format align calculations by slightly modifying supported formats
> > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > width since each plain has stride width * 1. This aligns with downstream
> > >
> > > plane
> > >
> > > > behavior which uses same approach for offset calculations.
> > > >
> > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > ---
> > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > >
> > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > >
> > > > - switch (pix->pixelformat) {
> > > > - case V4L2_PIX_FMT_UYVY:
> > > > - case V4L2_PIX_FMT_VYUY:
> > > > - case V4L2_PIX_FMT_YUYV:
> > > > - case V4L2_PIX_FMT_YVYU:
> > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > - break;
> > > > - case V4L2_PIX_FMT_YUV420:
> > > > - case V4L2_PIX_FMT_YVU420:
> > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > - break;
> > > > - }
> > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > }
> > > >
> > > > /*
> > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > {
> > > > unsigned int stride = chan->format.bytesperline;
> > > > unsigned int height = chan->format.height;
> > > > + unsigned int width = chan->format.width;
> > > >
> > > > chan->start_offset = 0;
> > > >
> > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > >
> > > > case V4L2_PIX_FMT_YUV420:
> > > > case V4L2_PIX_FMT_YVU420:
> > > > - chan->addr_offset_u = stride * height;
> > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > + chan->addr_offset_u = width * height;
> > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > >
> > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > chan->start_offset_v = chan->addr_offset_v;
> > > >
> > > > if (chan->vflip) {
> > > > - chan->start_offset += stride * (height - 1);
> > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > + chan->start_offset += width * (height - 1);
> > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > }
> > > > if (chan->hflip) {
> > > > - chan->start_offset += stride - 1;
> > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > + chan->start_offset += width - 1;
> > > > + chan->start_offset_u += (width / 2) - 1;
> > > > + chan->start_offset_v += (width / 2) - 1;
> > > > }
> > > > break;
> > > > }
> > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > };
> > > >
> > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > -{ \
> > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > - .bpp = BPP, \
> > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > +{ \
> > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > + .bit_width = BIT_WIDTH, \
> > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > + .bpp = BPP, \
> > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > }
> > > >
> > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > + /* YUV422 */
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > };
> > >
> > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > >
> > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > >
> >
> > No, this code is actually cleaner and in sync with what downstream
> > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > formats with fractional bytes per pixel, it is impossible to set there
> > 3/2 for example, which is used by YUV420.
> >
> > According to downstream code bytes_per_line =
> > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > different buffers so with offset calculation plain width must be used
> > (which matches downstream).
> >
>
> If you mean use of BPP by VI, I can propose removing bytesperline and
> sizeimage configuration from VI entirely and leave this to per-SoC
> fmt_align function which does this already anyway and guards every
> time those values are referred. This way there will be no instances
> where "places not being the actual bytes per line" comes true.
Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
>
> > > >
> > > > const struct tegra_vi_soc tegra20_vi_soc = {
> > > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > > index bfadde8858d4..5cbc0606ed6c 100644
> > > > --- a/drivers/staging/media/tegra-video/vi.h
> > > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > > @@ -281,7 +281,8 @@ enum tegra_image_dt {
> > > > * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> > > > * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> > > > * @code: media bus format code
> > > > - * @bpp: bytes per pixel (when stored in memory)
> > > > + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> > > > + * bits per pixel for Tegra20/Tegra30
> > > > * @img_fmt: image format (for CSI/Tegra210 only)
> > > > * @fourcc: V4L2 pixel format FCC identifier
> > > > */
> > > >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-01 7:51 ` Mikko Perttunen
@ 2025-10-01 7:59 ` Svyatoslav Ryhel
2025-10-02 4:00 ` Mikko Perttunen
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-01 7:59 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 10:51 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> > ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> > >
> > > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > >
> > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > Simplify format align calculations by slightly modifying supported formats
> > > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > > width since each plain has stride width * 1. This aligns with downstream
> > > >
> > > > plane
> > > >
> > > > > behavior which uses same approach for offset calculations.
> > > > >
> > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > ---
> > > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > > >
> > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > > >
> > > > > - switch (pix->pixelformat) {
> > > > > - case V4L2_PIX_FMT_UYVY:
> > > > > - case V4L2_PIX_FMT_VYUY:
> > > > > - case V4L2_PIX_FMT_YUYV:
> > > > > - case V4L2_PIX_FMT_YVYU:
> > > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > > - break;
> > > > > - case V4L2_PIX_FMT_YUV420:
> > > > > - case V4L2_PIX_FMT_YVU420:
> > > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > > - break;
> > > > > - }
> > > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > > }
> > > > >
> > > > > /*
> > > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > {
> > > > > unsigned int stride = chan->format.bytesperline;
> > > > > unsigned int height = chan->format.height;
> > > > > + unsigned int width = chan->format.width;
> > > > >
> > > > > chan->start_offset = 0;
> > > > >
> > > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > >
> > > > > case V4L2_PIX_FMT_YUV420:
> > > > > case V4L2_PIX_FMT_YVU420:
> > > > > - chan->addr_offset_u = stride * height;
> > > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > > + chan->addr_offset_u = width * height;
> > > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > > >
> > > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > chan->start_offset_v = chan->addr_offset_v;
> > > > >
> > > > > if (chan->vflip) {
> > > > > - chan->start_offset += stride * (height - 1);
> > > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > > + chan->start_offset += width * (height - 1);
> > > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > > }
> > > > > if (chan->hflip) {
> > > > > - chan->start_offset += stride - 1;
> > > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > > + chan->start_offset += width - 1;
> > > > > + chan->start_offset_u += (width / 2) - 1;
> > > > > + chan->start_offset_v += (width / 2) - 1;
> > > > > }
> > > > > break;
> > > > > }
> > > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > > };
> > > > >
> > > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > > -{ \
> > > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > - .bpp = BPP, \
> > > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > > +{ \
> > > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > > + .bit_width = BIT_WIDTH, \
> > > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > + .bpp = BPP, \
> > > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > }
> > > > >
> > > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > > + /* YUV422 */
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > > };
> > > >
> > > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > > >
> > > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > > >
> > >
> > > No, this code is actually cleaner and in sync with what downstream
> > > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > > formats with fractional bytes per pixel, it is impossible to set there
> > > 3/2 for example, which is used by YUV420.
> > >
> > > According to downstream code bytes_per_line =
> > > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > > different buffers so with offset calculation plain width must be used
> > > (which matches downstream).
> > >
> >
> > If you mean use of BPP by VI, I can propose removing bytesperline and
> > sizeimage configuration from VI entirely and leave this to per-SoC
> > fmt_align function which does this already anyway and guards every
> > time those values are referred. This way there will be no instances
> > where "places not being the actual bytes per line" comes true.
>
> Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
>
I am proposing you a solution, handle bytesperline and sizeimage in
per-SoC fmt_align function.
12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
plane and 2 for V plane, total is 12. "but explainable with a comment
and improve-able later" why then we cannot use 12 with a comment? this
is all arbitrary. Downstream is not wrong from this perspective, you
don't take into account that YUV420 is planar and it uses 3 planes a
whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
1/4 width which is width * 3/2
> >
> > > > >
> > > > > const struct tegra_vi_soc tegra20_vi_soc = {
> > > > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > > > index bfadde8858d4..5cbc0606ed6c 100644
> > > > > --- a/drivers/staging/media/tegra-video/vi.h
> > > > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > > > @@ -281,7 +281,8 @@ enum tegra_image_dt {
> > > > > * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> > > > > * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> > > > > * @code: media bus format code
> > > > > - * @bpp: bytes per pixel (when stored in memory)
> > > > > + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> > > > > + * bits per pixel for Tegra20/Tegra30
> > > > > * @img_fmt: image format (for CSI/Tegra210 only)
> > > > > * @fourcc: V4L2 pixel format FCC identifier
> > > > > */
> > > > >
>
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-10-01 6:38 ` Mikko Perttunen
@ 2025-10-01 15:23 ` Svyatoslav Ryhel
2025-10-02 4:03 ` Mikko Perttunen
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-01 15:23 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 09:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Wednesday, October 1, 2025 2:15 PM Svyatoslav Ryhel wrote:
> > ср, 1 жовт. 2025 р. о 08:04 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > >
> > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > Add support for MIPI CSI device and calibration logic found in Tegra20 and
> > > > Tegra30 SoC.
> > > >
> > > > To get CSI operational, an additional syncpoint was allocated to serve as
> > > > the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> > > > frame start events. That said, the frame capture function was refactored
> > > > to reflect the addition of the CSI syncpoint, and the CSI-specific
> > > > configuration is guarded by the presence of a passed CSI channel structure
> > > > pointer.
> > > >
> > > > The camera capture setup's configuration was reconsidered: the first two
> > > > writes must be done before tegra_channel_set_stream for MIPI calibration
> > > > to work properly; the third write was moved to VIP/CSI-specific functions
> > > > since it must be source-specific; the function was placed after
> > > > tegra_channel_set_stream so the initial sequence is preserved and expanded.
> > > >
> > > > CSI configuration sequences were added based on downstream 3.1 kernel
> > > > sources and adjusted to the existing video-tegra framework. Although
> > > > Tegra20 and Tegra30 have the same set of configurations, they differ by
> > > > the number of clocks used by CSI.
> > > >
> > > > Dropped the software syncpoint counters in favor of reading syncpoints
> > > > directly and passing the incremented value to the polling function. If the
> > > > syncpoint increase fails, the PP is reset. This change should prevent
> > > > possible race conditions.
> > > >
> > > > MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> > > > have no dedicated hardware block for these operations and use CSI. These
> > > > calls are used for both CSI and DSI to work properly, which is why MIPI
> > > > calibration cannot be contained within CSI. The pads passed to the
> > > > calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> > > > DSI-B (4).
> > > >
> > > > Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > ---
> > > > drivers/staging/media/tegra-video/csi.c | 19 +
> > > > drivers/staging/media/tegra-video/csi.h | 4 +
> > > > drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> > > > drivers/staging/media/tegra-video/vi.h | 2 -
> > > > drivers/staging/media/tegra-video/video.c | 6 +
> > > > 5 files changed, 592 insertions(+), 47 deletions(-)
> > > >
> > > > diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> > > > index 7d70478a07aa..92ee4c84a988 100644
> > > > --- a/drivers/staging/media/tegra-video/csi.c
> > > > +++ b/drivers/staging/media/tegra-video/csi.c
> > > > @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
> > > >
> > > > csi->dev = &pdev->dev;
> > > > csi->ops = csi->soc->ops;
> > > > +
> > > > + if (csi->soc->mipi_ops)
> > > > + tegra_mipi_add_provider(pdev->dev.of_node,
> > > > + csi->soc->mipi_ops);
> > >
> > > Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
> > >
> >
> > Your words tegra_mipi_remove_provider are unclear, should I add it and
> > where should I use it exactly? I can make devm version of
> > tegra_mipi_remove_provider by adding action there.
>
> Yes, devm_tegra_mipi_add_provider would be a good solution. My intent was that we should clean up the provider registration in an error case or when the CSI device is removed.
>
> FWIW, I've spent a little time looking at the refcounting situation, and it doesn't seem like the device refcount guarantees the driver is still bound. Will need to look at this further, but no need to block this series.
>
What should I release in remove? I have not found instance where
structures like provider were released in any way, unless they
allocate memory for it parts, in this case provider has only 2
pointers and does not allocate anything so release is not needed.
> >
> > > > +
> > > > + mutex_init(&csi->mipi_lock);
> > > > +
> > > > platform_set_drvdata(pdev, csi);
> > > > pm_runtime_enable(&pdev->dev);
> > > >
> > > > @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> > > > pm_runtime_disable(&pdev->dev);
> > > > }
> > > >
> > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > +extern const struct tegra_csi_soc tegra20_csi_soc;
> > > > +#endif
> > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > +extern const struct tegra_csi_soc tegra30_csi_soc;
> > > > +#endif
> > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > extern const struct tegra_csi_soc tegra210_csi_soc;
> > > > #endif
> > > >
> > > > static const struct of_device_id tegra_csi_of_id_table[] = {
> > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> > > > +#endif
> > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> > > > +#endif
> > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> > > > #endif
> > > > diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> > > > index 1550defb115a..422f30655945 100644
> > > > --- a/drivers/staging/media/tegra-video/csi.h
> > > > +++ b/drivers/staging/media/tegra-video/csi.h
> > > > @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> > > > * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> > > > *
> > > > * @ops: csi hardware operations
> > > > + * @mipi_ops: MIPI calibration operations
> > > > * @csi_max_channels: supported max streaming channels
> > > > * @clk_names: csi and cil clock names
> > > > * @num_clks: total clocks count
> > > > @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> > > > */
> > > > struct tegra_csi_soc {
> > > > const struct tegra_csi_ops *ops;
> > > > + const struct tegra_mipi_ops *mipi_ops;
> > > > unsigned int csi_max_channels;
> > > > const char * const *clk_names;
> > > > unsigned int num_clks;
> > > > @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> > > > * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> > > > * @soc: pointer to SoC data structure
> > > > * @ops: csi operations
> > > > + * @mipi_lock: for MIPI calibration operations
> > > > * @csi_chans: list head for CSI channels
> > > > */
> > > > struct tegra_csi {
> > > > @@ -150,6 +153,7 @@ struct tegra_csi {
> > > > struct regulator *vdd;
> > > > const struct tegra_csi_soc *soc;
> > > > const struct tegra_csi_ops *ops;
> > > > + struct mutex mipi_lock;
> > > > struct list_head csi_chans;
> > > > };
> > > >
> > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > index 8c9655ffa886..d99a04fa25af 100644
> > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > @@ -4,6 +4,9 @@
> > > > *
> > > > * Copyright (C) 2023 SKIDATA GmbH
> > > > * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > > + *
> > > > + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> > > > + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > */
> > > >
> > > > /*
> > > > @@ -12,10 +15,15 @@
> > > > */
> > > >
> > > > #include <linux/bitfield.h>
> > > > +#include <linux/clk.h>
> > > > +#include <linux/clk/tegra.h>
> > > > #include <linux/delay.h>
> > > > #include <linux/host1x.h>
> > > > +#include <linux/iopoll.h>
> > > > #include <linux/kernel.h>
> > > > #include <linux/kthread.h>
> > > > +#include <linux/pm_runtime.h>
> > > > +#include <linux/tegra-mipi-cal.h>
> > > > #include <linux/v4l2-mediabus.h>
> > > >
> > > > #include "vip.h"
> > > > @@ -42,6 +50,9 @@ enum tegra_vi_out {
> > > > #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> > > > #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
> > > >
> > > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> > > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> > > > +
> > > > #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> > > > #define VI_INPUT_FIELD_DETECT BIT(27)
> > > > #define VI_INPUT_BT656 BIT(25)
> > > > @@ -88,6 +99,8 @@ enum tegra_vi_out {
> > > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > /* TEGRA_VI_OUT_2 supported formats */
> > > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > >
> > > > #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> > > > @@ -152,8 +165,106 @@ enum tegra_vi_out {
> > > > #define TEGRA_VI_VI_RAISE 0x01ac
> > > > #define VI_VI_RAISE_ON_EDGE BIT(0)
> > > >
> > > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> > > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> > > > +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> > > > +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> > > > +
> > > > +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> > > > +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> > > > +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> > > > +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> > > > +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> > > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> > > > +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> > > > +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> > > > +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> > > > +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> > > > +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> > > > +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> > > > +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> > > > +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> > > > +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> > > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> > > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> > > > +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> > > > +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> > > > +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> > > > +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> > > > +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> > > > +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> > > > +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> > > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> > > > +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> > > > +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> > > > +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> > > > +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> > > > +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> > > > +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> > > > +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> > > > +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> > > > +#define CSI_PP_SINGLE_SHOT BIT(2)
> > > > +#define CSI_PP_NOP 0
> > > > +#define CSI_PP_ENABLE 1
> > > > +#define CSI_PP_DISABLE 2
> > > > +#define CSI_PP_RESET 3
> > > > +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> > > > +#define CSI_A_PHY_CIL_NOP 0x0
> > > > +#define CSI_A_PHY_CIL_ENABLE 0x1
> > > > +#define CSI_A_PHY_CIL_DISABLE 0x2
> > > > +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> > > > +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> > > > +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> > > > +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> > > > +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> > > > +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> > > > +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> > > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> > > > +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> > > > +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> > > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> > > > +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> > > > +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> > > > +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> > > > +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> > > > +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> > > > +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> > > > +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> > > > +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> > > > +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> > > > +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> > > > +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> > > > +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> > > > +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> > > > +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> > > > +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> > > > +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> > > > +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> > > > +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> > > > +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> > > > +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> > > > +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> > > > +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> > > > +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> > > > +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> > > > +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> > > > +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> > > > +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> > > > +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> > > > +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> > > > +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> > > > +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> > > > +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> > > > +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> > > > +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> > > > +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> > > > +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> > > > +
> > > > /* --------------------------------------------------------------------------
> > > > - * VI
> > > > + * Read and Write helpers
> > > > */
> > > >
> > > > static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> > > > @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> > > > writel(val, chan->vi->iomem + addr);
> > > > }
> > > >
> > > > +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> > > > +{
> > > > + return readl(chan->vi->iomem + addr);
> > > > +}
> > > > +
> > > > +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> > > > +{
> > > > + writel(val, csi_chan->csi->iomem + addr);
> > > > +}
> > > > +
> > > > +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> > > > +{
> > > > + return readl(csi_chan->csi->iomem + addr);
> > > > +}
> > > > +
> > > > +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> > > > +{
> > > > + writel(val, csi->iomem + addr);
> > > > +}
> > > > +
> > > > +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> > > > +{
> > > > + return readl(csi->iomem + addr);
> > > > +}
> > > > +
> > > > +/* --------------------------------------------------------------------------
> > > > + * VI
> > > > + */
> > > > +
> > > > /*
> > > > * Get the main input format (YUV/RGB...) and the YUV variant as values to
> > > > * be written into registers for the current VI input mbus code.
> > > > @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> > > > static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> > > > {
> > > > struct tegra_vi *vi = chan->vi;
> > > > - struct host1x_syncpt *out_sp;
> > > > + struct host1x_syncpt *out_sp, *fs_sp;
> > > >
> > > > out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > > if (!out_sp)
> > > > - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> > > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
> > > >
> > > > chan->mw_ack_sp[0] = out_sp;
> > > >
> > > > + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > > + if (!fs_sp)
> > > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> > > > +
> > > > + chan->frame_start_sp[0] = fs_sp;
> > > > +
> > > > return 0;
> > > > }
> > > >
> > > > static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> > > > {
> > > > host1x_syncpt_put(chan->mw_ack_sp[0]);
> > > > + host1x_syncpt_put(chan->frame_start_sp[0]);
> > > > }
> > > >
> > > > static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> > > > }
> > > >
> > > > static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> > > > - struct tegra_channel_buffer *buf)
> > > > + struct tegra_channel_buffer *buf,
> > > > + struct tegra_csi_channel *csi_chan)
> > > > {
> > > > + u32 val;
> > > > int err;
> > > >
> > > > - chan->next_out_sp_idx++;
> > > > -
> > > > tegra20_channel_vi_buffer_setup(chan, buf);
> > > >
> > > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > + if (csi_chan) {
> > > > + u32 port = csi_chan->csi_port_nums[0] & 1;
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> > > > +
> > > > + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> > > > + do {
> > > > + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> > > > + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > + } while (err == -ERESTARTSYS);
> > >
> > > This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
> > >
> >
> > This is caused by dma_fence_wait_timeout being unconditionally
> > interruptible. I do not want to touch host1x stuff, this patchset
> > already takes too much resources.
>
> Sure, I don't think we need to change host1x. Since this function is only called from non-user context, even if the wait is interruptible it should never be actually interrupted. So I think you can just drop the ERESTARTSYS handling.
>
> >
> > > > +
> > > > + if (err) {
> > > > + if (err != -ERESTARTSYS)
> > > > + dev_err_ratelimited(&chan->video.dev,
> > > > + "frame start syncpt timeout: %d\n", err);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> > > > + goto exit;
> > > > + }
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > + CSI_PP_DISABLE);
> > > > + } else {
> > > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > + }
> > > > +
> > > > + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > > + do {
> > > > + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> > > > + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > + } while (err == -ERESTARTSYS);
> > > >
> > > > - /* Wait for syncpt counter to reach frame start event threshold */
> > > > - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> > > > - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > if (err) {
> > > > - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> > > > - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> > > > - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> > > > - return err;
> > > > + if (err != -ERESTARTSYS)
> > > > + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> > > > + goto exit;
> > > > }
> > > >
> > > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > > - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > + if (!csi_chan)
> > > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > > + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > > >
> > > > +exit:
> > > > release_buffer(chan, buf, VB2_BUF_STATE_DONE);
> > > >
> > > > - return 0;
> > > > + return err;
> > > > }
> > > >
> > > > static int tegra20_chan_capture_kthread_start(void *data)
> > > > {
> > > > struct tegra_vi_channel *chan = data;
> > > > struct tegra_channel_buffer *buf;
> > > > + struct v4l2_subdev *csi_subdev = NULL;
> > > > + struct tegra_csi_channel *csi_chan = NULL;
> > > > unsigned int retries = 0;
> > > > int err = 0;
> > > >
> > > > + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> > > > + if (csi_subdev)
> > > > + csi_chan = to_csi_chan(csi_subdev);
> > > > +
> > > > while (1) {
> > > > /*
> > > > * Source is not streaming if error is non-zero.
> > > > @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> > > > list_del_init(&buf->queue);
> > > > spin_unlock(&chan->start_lock);
> > > >
> > > > - err = tegra20_channel_capture_frame(chan, buf);
> > > > + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> > > > if (!err) {
> > > > retries = 0;
> > > > continue;
> > > > @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> > > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > > - int main_output_format;
> > > > - int yuv_output_format;
> > > > -
> > > > - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> > > > -
> > > > - /*
> > > > - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > > - * for luminance, which is the default and means not to touch
> > > > - * anything.
> > > > - */
> > > > - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > > - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > > - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > > -
> > > > - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > > - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > > > -
> > > > - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> > > > - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
> > > >
> > > > /* Set up frame size */
> > > > tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> > > > @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> > > > struct media_pipeline *pipe = &chan->video.pipe;
> > > > int err;
> > > >
> > > > - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > > -
> > > > err = video_device_pipeline_start(&chan->video, pipe);
> > > > if (err)
> > > > goto error_pipeline_start;
> > > >
> > > > - tegra20_camera_capture_setup(chan);
> > > > + /*
> > > > + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > > + * for luminance, which is the default and means not to touch
> > > > + * anything.
> > > > + */
> > > > + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > > + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > > + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > > +
> > > > + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > > + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > > >
> > > > err = tegra_channel_set_stream(chan, true);
> > > > if (err)
> > > > goto error_set_stream;
> > > >
> > > > + tegra20_camera_capture_setup(chan);
> > > > +
> > > > chan->sequence = 0;
> > > >
> > > > chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> > > > @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> > > > .has_h_v_flip = true,
> > > > };
> > > >
> > > > +/* --------------------------------------------------------------------------
> > > > + * MIPI Calibration
> > > > + */
> > > > +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> > > > +{
> > > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > > + unsigned int port = mipi->pads;
> > > > + u32 value;
> > > > + int ret;
> > > > +
> > > > + guard(mutex)(&csi->mipi_lock);
> > > > +
> > > > + ret = pm_runtime_resume_and_get(csi->dev);
> > > > + if (ret < 0) {
> > > > + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> > > > + return ret;
> > > > + }
> > > > +
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> > > > + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> > > > + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> > > > + CSI_CIL_MIPI_CAL_TERMOS(0));
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> > > > + CSI_PAD_DRIV_DN_REF(5) |
> > > > + CSI_PAD_DRIV_UP_REF(7) |
> > > > + CSI_PAD_TERM_REF(0));
> > > > +
> > > > + /* CSI B */
> > > > + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > > +
> > > > + if (port == PORT_B)
> > > > + value |= CSI_CIL_MIPI_CAL_SEL_B;
> > > > +
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> > > > +
> > > > + /* CSI A */
> > > > + value = CSI_CIL_MIPI_CAL_STARTCAL |
> > > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > > +
> > > > + if (port == PORT_A)
> > > > + value |= CSI_CIL_MIPI_CAL_SEL_A;
> > > > +
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> > > > +
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> > > > +{
> > > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > > + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> > > > + unsigned int port = mipi->pads;
> > > > + u32 value, pp = 0, cil = 0;
> > > > + int ret;
> > > > +
> > > > + /* This part is only for CSI */
> > > > + if (port > PORT_B) {
> > > > + pm_runtime_put(csi->dev);
> > > > +
> > > > + return 0;
> > > > + }
> > > > +
> > > > + guard(mutex)(&csi->mipi_lock);
> > > > +
> > > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > > + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> > > > + if (ret < 0) {
> > > > + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> > > > + goto exit;
> > > > + }
> > > > +
> > > > + /* clear status */
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > > + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> > > > + if (ret < 0) {
> > > > + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> > > > + goto exit;
> > > > + }
> > > > +
> > > > + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > > + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> > > > + if (pp | cil) {
> > > > + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> > > > + ret = -EINVAL;
> > > > + goto exit;
> > > > + }
> > > > +
> > > > +exit:
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> > > > +
> > > > + /* un-select to avoid interference with DSI */
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > > +
> > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> > > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > > +
> > > > + pm_runtime_put(csi->dev);
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> > > > + .start_calibration = tegra20_start_pad_calibration,
> > > > + .finish_calibration = tegra20_finish_pad_calibration,
> > > > +};
> > > > +
> > > > +/* --------------------------------------------------------------------------
> > > > + * CSI
> > > > + */
> > > > +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> > > > +{
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> > > > +}
> > > > +
> > > > +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> > > > + u8 portno)
> > > > +{
> > > > + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> > > > + int width = vi_chan->format.width;
> > > > + int height = vi_chan->format.height;
> > > > + u32 data_type = vi_chan->fmtinfo->img_dt;
> > > > + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> > > > + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> > > > +
> > > > + unsigned int main_output_format, yuv_output_format;
> > > > + unsigned int port = portno & 1;
> > > > + u32 value;
> > > > +
> > > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > > > +
> > > > + switch (data_type) {
> > > > + case TEGRA_IMAGE_DT_RAW8:
> > > > + case TEGRA_IMAGE_DT_RAW10:
> > > > + output_channel = TEGRA_VI_OUT_2;
> > > > + if (port == PORT_A)
> > > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> > > > + else
> > > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> > > > + break;
> > > > + }
> > > > +
> > > > + tegra20_csi_capture_clean(csi_chan);
> > > > +
> > > > + /* CSI port cleanup */
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> > > > + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> > > > + CSI_PP_EXP_FRAME_HEIGHT(height) |
> > > > + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> > > > + CSI_PP_LINE_TIMEOUT_ENABLE);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> > > > + CSI_PP_OUTPUT_FORMAT_PIXEL |
> > > > + CSI_PP_DATA_TYPE(data_type) |
> > > > + CSI_PP_CRC_CHECK_ENABLE |
> > > > + CSI_PP_WORD_COUNT_HEADER |
> > > > + CSI_PP_DATA_IDENTIFIER_ENABLE |
> > > > + CSI_PP_PACKET_HEADER_SENT |
> > > > + port);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> > > > + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> > > > + (csi_chan->numlanes - 1));
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> > > > + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> > > > + 0x5); /* Clock settle time */
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> > > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > > + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> > > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> > > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > > + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> > > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > > +
> > > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> > > > + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > + CSI_PP_DISABLE);
> > > > +
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > + yuv_output_format | main_output_format);
> > > > +
> > > > + return 0;
> > > > +};
> > > > +
> > > > +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> > > > +{
> > > > + struct tegra_csi *csi = csi_chan->csi;
> > > > + unsigned int port = portno & 1;
> > > > + u32 value;
> > > > +
> > > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> > > > +
> > > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> > > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > > +
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > + CSI_PP_DISABLE);
> > > > +
> > > > + if (csi_chan->numlanes == 4) {
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> > > > + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> > > > + } else {
> > > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> > > > + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > > + }
> > > > +}
> > > > +
> > > > +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> > > > +{
> > > > + u8 *portnos = csi_chan->csi_port_nums;
> > > > + int ret, i;
> > > > +
> > > > + for (i = 0; i < csi_chan->numgangports; i++) {
> > > > + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> > > > + if (ret)
> > > > + goto stream_start_fail;
> > > > + }
> > > > +
> > > > + return 0;
> > > > +
> > > > +stream_start_fail:
> > > > + for (i = i - 1; i >= 0; i--)
> > > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > > +
> > > > + return ret;
> > > > +}
> > > > +
> > > > +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> > > > +{
> > > > + u8 *portnos = csi_chan->csi_port_nums;
> > > > + int i;
> > > > +
> > > > + for (i = 0; i < csi_chan->numgangports; i++)
> > > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > > +}
> > > > +
> > > > +static const struct tegra_csi_ops tegra20_csi_ops = {
> > > > + .csi_start_streaming = tegra20_csi_start_streaming,
> > > > + .csi_stop_streaming = tegra20_csi_stop_streaming,
> > > > +};
> > > > +
> > > > +static const char * const tegra20_csi_clks[] = {
> > > > + NULL,
> > > > +};
> > > > +
> > > > +const struct tegra_csi_soc tegra20_csi_soc = {
> > > > + .ops = &tegra20_csi_ops,
> > > > + .mipi_ops = &tegra20_mipi_ops,
> > > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > > + .clk_names = tegra20_csi_clks,
> > > > + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> > > > +};
> > > > +
> > > > +static const char * const tegra30_csi_clks[] = {
> > > > + "csi",
> > > > + "csia-pad",
> > > > + "csib-pad",
> > > > +};
> > > > +
> > > > +const struct tegra_csi_soc tegra30_csi_soc = {
> > > > + .ops = &tegra20_csi_ops,
> > > > + .mipi_ops = &tegra20_mipi_ops,
> > > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > > + .clk_names = tegra30_csi_clks,
> > > > + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> > > > +};
> > > > +
> > > > /* --------------------------------------------------------------------------
> > > > * VIP
> > > > */
> > > > @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > > - unsigned int main_input_format;
> > > > - unsigned int yuv_input_format;
> > > > + unsigned int main_input_format, yuv_input_format;
> > > > + unsigned int main_output_format, yuv_output_format;
> > > >
> > > > tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> > > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > > >
> > > > tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
> > > >
> > > > @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > > >
> > > > tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
> > > >
> > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > + yuv_output_format | main_output_format);
> > > > +
> > > > return 0;
> > > > }
> > > >
> > > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > > index 5cbc0606ed6c..bad55e0bd313 100644
> > > > --- a/drivers/staging/media/tegra-video/vi.h
> > > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > > @@ -125,7 +125,6 @@ struct tegra_vi {
> > > > * frame through host1x syncpoint counters (On Tegra20 used for the
> > > > * OUT_1 syncpt)
> > > > * @sp_incr_lock: protects cpu syncpoint increment.
> > > > - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> > > > *
> > > > * @kthread_start_capture: kthread to start capture of single frame when
> > > > * vb buffer is available. This thread programs VI CSI hardware
> > > > @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> > > > struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> > > > /* protects the cpu syncpoint increment */
> > > > spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> > > > - u32 next_out_sp_idx;
> > > >
> > > > struct task_struct *kthread_start_capture;
> > > > wait_queue_head_t start_wait;
> > > > diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> > > > index 6fe8d5301b9c..9f2bddc460bf 100644
> > > > --- a/drivers/staging/media/tegra-video/video.c
> > > > +++ b/drivers/staging/media/tegra-video/video.c
> > > > @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> > > > { .compatible = "nvidia,tegra20-vip", },
> > > > { .compatible = "nvidia,tegra20-vi", },
> > > > #endif
> > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > + { .compatible = "nvidia,tegra20-csi", },
> > > > +#endif
> > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > + { .compatible = "nvidia,tegra30-csi", },
> > > > +#endif
> > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > { .compatible = "nvidia,tegra210-csi", },
> > > > { .compatible = "nvidia,tegra210-vi", },
> > > >
> > >
> > >
> > >
> > >
>
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP
2025-09-25 15:16 ` [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP Svyatoslav Ryhel
@ 2025-10-02 1:19 ` Rob Herring (Arm)
0 siblings, 0 replies; 50+ messages in thread
From: Rob Herring (Arm) @ 2025-10-02 1:19 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Maarten Lankhorst, Michael Turquette, linux-media, Diogo Ivo,
Thierry Reding, linux-staging, devicetree, Arnd Bergmann,
dri-devel, linux-tegra, Dmitry Osipenko, linux-gpio,
Jonathan Hunter, Krzysztof Kozlowski, Stephen Boyd, Conor Dooley,
Sowjanya Komatineni, Jonas Schwöbel, Simona Vetter,
Aaron Kling, David Airlie, linux-clk, Mikko Perttunen,
Prashant Gaikwad, Thomas Zimmermann, Linus Walleij, linux-kernel,
Luca Ceresoli, Maxime Ripard, Greg Kroah-Hartman,
Mauro Carvalho Chehab, Charan Pedumuru
On Thu, 25 Sep 2025 18:16:30 +0300, Svyatoslav Ryhel wrote:
> Existing Parallel VI interface schema for Tegra20 is fully compatible with
> Tegra30; hence, lets reuse it by setting fallback for Tegra30.
>
> Adjust existing VI schema to reflect that Tegra20 VI is compatible with
> Tegra30 by setting a fallback for Tegra30. Additionally, switch to using
> an enum instead of list of const.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> .../display/tegra/nvidia,tegra20-vi.yaml | 19 ++++++++++++-------
> .../display/tegra/nvidia,tegra20-vip.yaml | 9 +++++++--
> 2 files changed, 19 insertions(+), 9 deletions(-)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
2025-09-25 15:16 ` [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI Svyatoslav Ryhel
@ 2025-10-02 1:52 ` Rob Herring
2025-10-02 5:14 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Rob Herring @ 2025-10-02 1:52 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann, dri-devel,
devicetree, linux-tegra, linux-kernel, linux-media, linux-clk,
linux-gpio, linux-staging
On Thu, Sep 25, 2025 at 06:16:46PM +0300, Svyatoslav Ryhel wrote:
> Document CSI HW block found in Tegra20 and Tegra30 SoC.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> ---
> .../display/tegra/nvidia,tegra20-csi.yaml | 135 ++++++++++++++++++
> 1 file changed, 135 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
>
> diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> new file mode 100644
> index 000000000000..817b3097846b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> @@ -0,0 +1,135 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-csi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NVIDIA Tegra20 CSI controller
> +
> +maintainers:
> + - Svyatoslav Ryhel <clamor95@gmail.com>
> +
> +properties:
> + compatible:
> + enum:
> + - nvidia,tegra20-csi
> + - nvidia,tegra30-csi
> +
> + reg:
> + maxItems: 1
> +
> + clocks: true
> + clock-names: true
> +
> + avdd-dsi-csi-supply:
> + description: DSI/CSI power supply. Must supply 1.2 V.
> +
> + power-domains:
> + maxItems: 1
> +
> + "#nvidia,mipi-calibrate-cells":
> + description:
> + The number of cells in a MIPI calibration specifier. Should be 1.
> + The single cell specifies an id of the pad that need to be
> + calibrated for a given device. Valid pad ids for receiver would be
> + 0 for CSI-A; 1 for CSI-B; 2 for DSI-A and 3 for DSI-B.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + const: 1
Sorry I didn't bring this up before, but is this ever not 1? If it is
fixed, then you don't really need the property. I prefer it just be
fixed rather than getting a bunch of vendor specific #foo-cells.
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> +patternProperties:
> + "^channel@[0-1]$":
> + type: object
> + description: channel 0 represents CSI-A and 1 represents CSI-B
> + additionalProperties: false
> +
> + properties:
> + reg:
> + maximum: 1
> +
> + nvidia,mipi-calibrate:
> + description: Should contain a phandle and a specifier specifying
> + which pad is used by this CSI channel and needs to be calibrated.
> + $ref: /schemas/types.yaml#/definitions/phandle-array
Sounds like only one entry? Then 'maxItems: 1' is needed. If you drop
#nvidia,mipi-calibrate-cells, then you need to define the arg size too:
items:
- items:
- description: phandle to ...
- description: what the arg contains.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-01 7:59 ` Svyatoslav Ryhel
@ 2025-10-02 4:00 ` Mikko Perttunen
2025-10-02 5:41 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-02 4:00 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
On Wednesday, October 1, 2025 4:59 PM Svyatoslav Ryhel wrote:
> ср, 1 жовт. 2025 р. о 10:51 Mikko Perttunen <mperttunen@nvidia.com> пише:
> >
> > On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> > > ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> > > >
> > > > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > > >
> > > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > > Simplify format align calculations by slightly modifying supported formats
> > > > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > > > width since each plain has stride width * 1. This aligns with downstream
> > > > >
> > > > > plane
> > > > >
> > > > > > behavior which uses same approach for offset calculations.
> > > > > >
> > > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > > ---
> > > > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > > > >
> > > > > > - switch (pix->pixelformat) {
> > > > > > - case V4L2_PIX_FMT_UYVY:
> > > > > > - case V4L2_PIX_FMT_VYUY:
> > > > > > - case V4L2_PIX_FMT_YUYV:
> > > > > > - case V4L2_PIX_FMT_YVYU:
> > > > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > > > - break;
> > > > > > - case V4L2_PIX_FMT_YUV420:
> > > > > > - case V4L2_PIX_FMT_YVU420:
> > > > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > > > - break;
> > > > > > - }
> > > > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > {
> > > > > > unsigned int stride = chan->format.bytesperline;
> > > > > > unsigned int height = chan->format.height;
> > > > > > + unsigned int width = chan->format.width;
> > > > > >
> > > > > > chan->start_offset = 0;
> > > > > >
> > > > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > >
> > > > > > case V4L2_PIX_FMT_YUV420:
> > > > > > case V4L2_PIX_FMT_YVU420:
> > > > > > - chan->addr_offset_u = stride * height;
> > > > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > > > + chan->addr_offset_u = width * height;
> > > > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > > > >
> > > > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > chan->start_offset_v = chan->addr_offset_v;
> > > > > >
> > > > > > if (chan->vflip) {
> > > > > > - chan->start_offset += stride * (height - 1);
> > > > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > > > + chan->start_offset += width * (height - 1);
> > > > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > > > }
> > > > > > if (chan->hflip) {
> > > > > > - chan->start_offset += stride - 1;
> > > > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > > > + chan->start_offset += width - 1;
> > > > > > + chan->start_offset_u += (width / 2) - 1;
> > > > > > + chan->start_offset_v += (width / 2) - 1;
> > > > > > }
> > > > > > break;
> > > > > > }
> > > > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > > > };
> > > > > >
> > > > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > > > -{ \
> > > > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > - .bpp = BPP, \
> > > > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > > > +{ \
> > > > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > > > + .bit_width = BIT_WIDTH, \
> > > > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > + .bpp = BPP, \
> > > > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > }
> > > > > >
> > > > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > > > + /* YUV422 */
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > > > };
> > > > >
> > > > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > > > >
> > > > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > > > >
> > > >
> > > > No, this code is actually cleaner and in sync with what downstream
> > > > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > > > formats with fractional bytes per pixel, it is impossible to set there
> > > > 3/2 for example, which is used by YUV420.
> > > >
> > > > According to downstream code bytes_per_line =
> > > > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > > > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > > > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > > > different buffers so with offset calculation plain width must be used
> > > > (which matches downstream).
> > > >
> > >
> > > If you mean use of BPP by VI, I can propose removing bytesperline and
> > > sizeimage configuration from VI entirely and leave this to per-SoC
> > > fmt_align function which does this already anyway and guards every
> > > time those values are referred. This way there will be no instances
> > > where "places not being the actual bytes per line" comes true.
> >
> > Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
> >
>
> I am proposing you a solution, handle bytesperline and sizeimage in
> per-SoC fmt_align function.
Ok, I think that sounds good. It's good to consolidate the calculation in one place.
>
> 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> plane and 2 for V plane, total is 12. "but explainable with a comment
> and improve-able later" why then we cannot use 12 with a comment? this
> is all arbitrary. Downstream is not wrong from this perspective, you
> don't take into account that YUV420 is planar and it uses 3 planes a
> whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> 1/4 width which is width * 3/2
Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
>
> > >
> > > > > >
> > > > > > const struct tegra_vi_soc tegra20_vi_soc = {
> > > > > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > > > > index bfadde8858d4..5cbc0606ed6c 100644
> > > > > > --- a/drivers/staging/media/tegra-video/vi.h
> > > > > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > > > > @@ -281,7 +281,8 @@ enum tegra_image_dt {
> > > > > > * @img_dt: MIPI CSI-2 data type (for CSI-2 only)
> > > > > > * @bit_width: format width in bits per component (for CSI/Tegra210 only)
> > > > > > * @code: media bus format code
> > > > > > - * @bpp: bytes per pixel (when stored in memory)
> > > > > > + * @bpp: bytes per pixel (when stored in memory) for Tegra210,
> > > > > > + * bits per pixel for Tegra20/Tegra30
> > > > > > * @img_fmt: image format (for CSI/Tegra210 only)
> > > > > > * @fourcc: V4L2 pixel format FCC identifier
> > > > > > */
> > > > > >
> >
> >
> >
> >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-10-01 15:23 ` Svyatoslav Ryhel
@ 2025-10-02 4:03 ` Mikko Perttunen
0 siblings, 0 replies; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-02 4:03 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
On Thursday, October 2, 2025 12:23 AM Svyatoslav Ryhel wrote:
> ср, 1 жовт. 2025 р. о 09:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> >
> > On Wednesday, October 1, 2025 2:15 PM Svyatoslav Ryhel wrote:
> > > ср, 1 жовт. 2025 р. о 08:04 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > >
> > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > Add support for MIPI CSI device and calibration logic found in Tegra20 and
> > > > > Tegra30 SoC.
> > > > >
> > > > > To get CSI operational, an additional syncpoint was allocated to serve as
> > > > > the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> > > > > frame start events. That said, the frame capture function was refactored
> > > > > to reflect the addition of the CSI syncpoint, and the CSI-specific
> > > > > configuration is guarded by the presence of a passed CSI channel structure
> > > > > pointer.
> > > > >
> > > > > The camera capture setup's configuration was reconsidered: the first two
> > > > > writes must be done before tegra_channel_set_stream for MIPI calibration
> > > > > to work properly; the third write was moved to VIP/CSI-specific functions
> > > > > since it must be source-specific; the function was placed after
> > > > > tegra_channel_set_stream so the initial sequence is preserved and expanded.
> > > > >
> > > > > CSI configuration sequences were added based on downstream 3.1 kernel
> > > > > sources and adjusted to the existing video-tegra framework. Although
> > > > > Tegra20 and Tegra30 have the same set of configurations, they differ by
> > > > > the number of clocks used by CSI.
> > > > >
> > > > > Dropped the software syncpoint counters in favor of reading syncpoints
> > > > > directly and passing the incremented value to the polling function. If the
> > > > > syncpoint increase fails, the PP is reset. This change should prevent
> > > > > possible race conditions.
> > > > >
> > > > > MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> > > > > have no dedicated hardware block for these operations and use CSI. These
> > > > > calls are used for both CSI and DSI to work properly, which is why MIPI
> > > > > calibration cannot be contained within CSI. The pads passed to the
> > > > > calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> > > > > DSI-B (4).
> > > > >
> > > > > Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > > Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > ---
> > > > > drivers/staging/media/tegra-video/csi.c | 19 +
> > > > > drivers/staging/media/tegra-video/csi.h | 4 +
> > > > > drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> > > > > drivers/staging/media/tegra-video/vi.h | 2 -
> > > > > drivers/staging/media/tegra-video/video.c | 6 +
> > > > > 5 files changed, 592 insertions(+), 47 deletions(-)
> > > > >
> > > > > diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> > > > > index 7d70478a07aa..92ee4c84a988 100644
> > > > > --- a/drivers/staging/media/tegra-video/csi.c
> > > > > +++ b/drivers/staging/media/tegra-video/csi.c
> > > > > @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
> > > > >
> > > > > csi->dev = &pdev->dev;
> > > > > csi->ops = csi->soc->ops;
> > > > > +
> > > > > + if (csi->soc->mipi_ops)
> > > > > + tegra_mipi_add_provider(pdev->dev.of_node,
> > > > > + csi->soc->mipi_ops);
> > > >
> > > > Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
> > > >
> > >
> > > Your words tegra_mipi_remove_provider are unclear, should I add it and
> > > where should I use it exactly? I can make devm version of
> > > tegra_mipi_remove_provider by adding action there.
> >
> > Yes, devm_tegra_mipi_add_provider would be a good solution. My intent was that we should clean up the provider registration in an error case or when the CSI device is removed.
> >
> > FWIW, I've spent a little time looking at the refcounting situation, and it doesn't seem like the device refcount guarantees the driver is still bound. Will need to look at this further, but no need to block this series.
> >
>
> What should I release in remove? I have not found instance where
> structures like provider were released in any way, unless they
> allocate memory for it parts, in this case provider has only 2
> pointers and does not allocate anything so release is not needed.
The provider (global structure) fields should be set to NULL, so that if a driver later calls tegra_mipi_request it will get an error instead of invalid pointers.
>
> > >
> > > > > +
> > > > > + mutex_init(&csi->mipi_lock);
> > > > > +
> > > > > platform_set_drvdata(pdev, csi);
> > > > > pm_runtime_enable(&pdev->dev);
> > > > >
> > > > > @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> > > > > pm_runtime_disable(&pdev->dev);
> > > > > }
> > > > >
> > > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > > +extern const struct tegra_csi_soc tegra20_csi_soc;
> > > > > +#endif
> > > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > > +extern const struct tegra_csi_soc tegra30_csi_soc;
> > > > > +#endif
> > > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > > extern const struct tegra_csi_soc tegra210_csi_soc;
> > > > > #endif
> > > > >
> > > > > static const struct of_device_id tegra_csi_of_id_table[] = {
> > > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > > + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> > > > > +#endif
> > > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > > + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> > > > > +#endif
> > > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > > { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> > > > > #endif
> > > > > diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> > > > > index 1550defb115a..422f30655945 100644
> > > > > --- a/drivers/staging/media/tegra-video/csi.h
> > > > > +++ b/drivers/staging/media/tegra-video/csi.h
> > > > > @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> > > > > * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> > > > > *
> > > > > * @ops: csi hardware operations
> > > > > + * @mipi_ops: MIPI calibration operations
> > > > > * @csi_max_channels: supported max streaming channels
> > > > > * @clk_names: csi and cil clock names
> > > > > * @num_clks: total clocks count
> > > > > @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> > > > > */
> > > > > struct tegra_csi_soc {
> > > > > const struct tegra_csi_ops *ops;
> > > > > + const struct tegra_mipi_ops *mipi_ops;
> > > > > unsigned int csi_max_channels;
> > > > > const char * const *clk_names;
> > > > > unsigned int num_clks;
> > > > > @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> > > > > * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> > > > > * @soc: pointer to SoC data structure
> > > > > * @ops: csi operations
> > > > > + * @mipi_lock: for MIPI calibration operations
> > > > > * @csi_chans: list head for CSI channels
> > > > > */
> > > > > struct tegra_csi {
> > > > > @@ -150,6 +153,7 @@ struct tegra_csi {
> > > > > struct regulator *vdd;
> > > > > const struct tegra_csi_soc *soc;
> > > > > const struct tegra_csi_ops *ops;
> > > > > + struct mutex mipi_lock;
> > > > > struct list_head csi_chans;
> > > > > };
> > > > >
> > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > index 8c9655ffa886..d99a04fa25af 100644
> > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > @@ -4,6 +4,9 @@
> > > > > *
> > > > > * Copyright (C) 2023 SKIDATA GmbH
> > > > > * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > > > > + *
> > > > > + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > > > > */
> > > > >
> > > > > /*
> > > > > @@ -12,10 +15,15 @@
> > > > > */
> > > > >
> > > > > #include <linux/bitfield.h>
> > > > > +#include <linux/clk.h>
> > > > > +#include <linux/clk/tegra.h>
> > > > > #include <linux/delay.h>
> > > > > #include <linux/host1x.h>
> > > > > +#include <linux/iopoll.h>
> > > > > #include <linux/kernel.h>
> > > > > #include <linux/kthread.h>
> > > > > +#include <linux/pm_runtime.h>
> > > > > +#include <linux/tegra-mipi-cal.h>
> > > > > #include <linux/v4l2-mediabus.h>
> > > > >
> > > > > #include "vip.h"
> > > > > @@ -42,6 +50,9 @@ enum tegra_vi_out {
> > > > > #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> > > > > #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
> > > > >
> > > > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> > > > > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> > > > > +
> > > > > #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> > > > > #define VI_INPUT_FIELD_DETECT BIT(27)
> > > > > #define VI_INPUT_BT656 BIT(25)
> > > > > @@ -88,6 +99,8 @@ enum tegra_vi_out {
> > > > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > > #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > > /* TEGRA_VI_OUT_2 supported formats */
> > > > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > > #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > > > >
> > > > > #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> > > > > @@ -152,8 +165,106 @@ enum tegra_vi_out {
> > > > > #define TEGRA_VI_VI_RAISE 0x01ac
> > > > > #define VI_VI_RAISE_ON_EDGE BIT(0)
> > > > >
> > > > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> > > > > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> > > > > +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> > > > > +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> > > > > +
> > > > > +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> > > > > +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> > > > > +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> > > > > +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> > > > > +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> > > > > +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> > > > > +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> > > > > +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> > > > > +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> > > > > +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> > > > > +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> > > > > +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> > > > > +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> > > > > +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> > > > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> > > > > +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> > > > > +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> > > > > +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> > > > > +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> > > > > +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> > > > > +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> > > > > +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> > > > > +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> > > > > +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> > > > > +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> > > > > +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> > > > > +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> > > > > +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> > > > > +#define CSI_PP_SINGLE_SHOT BIT(2)
> > > > > +#define CSI_PP_NOP 0
> > > > > +#define CSI_PP_ENABLE 1
> > > > > +#define CSI_PP_DISABLE 2
> > > > > +#define CSI_PP_RESET 3
> > > > > +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> > > > > +#define CSI_A_PHY_CIL_NOP 0x0
> > > > > +#define CSI_A_PHY_CIL_ENABLE 0x1
> > > > > +#define CSI_A_PHY_CIL_DISABLE 0x2
> > > > > +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> > > > > +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> > > > > +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> > > > > +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> > > > > +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> > > > > +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> > > > > +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> > > > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> > > > > +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> > > > > +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> > > > > +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> > > > > +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> > > > > +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> > > > > +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> > > > > +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> > > > > +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> > > > > +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> > > > > +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> > > > > +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> > > > > +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> > > > > +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> > > > > +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> > > > > +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> > > > > +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> > > > > +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> > > > > +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> > > > > +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> > > > > +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> > > > > +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> > > > > +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> > > > > +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> > > > > +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> > > > > +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> > > > > +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> > > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> > > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> > > > > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> > > > > +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> > > > > +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> > > > > +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> > > > > +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> > > > > +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> > > > > +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> > > > > +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> > > > > +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> > > > > +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> > > > > +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> > > > > +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> > > > > +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> > > > > +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> > > > > +
> > > > > /* --------------------------------------------------------------------------
> > > > > - * VI
> > > > > + * Read and Write helpers
> > > > > */
> > > > >
> > > > > static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> > > > > @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> > > > > writel(val, chan->vi->iomem + addr);
> > > > > }
> > > > >
> > > > > +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> > > > > +{
> > > > > + return readl(chan->vi->iomem + addr);
> > > > > +}
> > > > > +
> > > > > +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> > > > > +{
> > > > > + writel(val, csi_chan->csi->iomem + addr);
> > > > > +}
> > > > > +
> > > > > +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> > > > > +{
> > > > > + return readl(csi_chan->csi->iomem + addr);
> > > > > +}
> > > > > +
> > > > > +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> > > > > +{
> > > > > + writel(val, csi->iomem + addr);
> > > > > +}
> > > > > +
> > > > > +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> > > > > +{
> > > > > + return readl(csi->iomem + addr);
> > > > > +}
> > > > > +
> > > > > +/* --------------------------------------------------------------------------
> > > > > + * VI
> > > > > + */
> > > > > +
> > > > > /*
> > > > > * Get the main input format (YUV/RGB...) and the YUV variant as values to
> > > > > * be written into registers for the current VI input mbus code.
> > > > > @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> > > > > static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> > > > > {
> > > > > struct tegra_vi *vi = chan->vi;
> > > > > - struct host1x_syncpt *out_sp;
> > > > > + struct host1x_syncpt *out_sp, *fs_sp;
> > > > >
> > > > > out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > > > if (!out_sp)
> > > > > - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> > > > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
> > > > >
> > > > > chan->mw_ack_sp[0] = out_sp;
> > > > >
> > > > > + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > > > > + if (!fs_sp)
> > > > > + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> > > > > +
> > > > > + chan->frame_start_sp[0] = fs_sp;
> > > > > +
> > > > > return 0;
> > > > > }
> > > > >
> > > > > static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> > > > > {
> > > > > host1x_syncpt_put(chan->mw_ack_sp[0]);
> > > > > + host1x_syncpt_put(chan->frame_start_sp[0]);
> > > > > }
> > > > >
> > > > > static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> > > > > }
> > > > >
> > > > > static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> > > > > - struct tegra_channel_buffer *buf)
> > > > > + struct tegra_channel_buffer *buf,
> > > > > + struct tegra_csi_channel *csi_chan)
> > > > > {
> > > > > + u32 val;
> > > > > int err;
> > > > >
> > > > > - chan->next_out_sp_idx++;
> > > > > -
> > > > > tegra20_channel_vi_buffer_setup(chan, buf);
> > > > >
> > > > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > > + if (csi_chan) {
> > > > > + u32 port = csi_chan->csi_port_nums[0] & 1;
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > > + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> > > > > +
> > > > > + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> > > > > + do {
> > > > > + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> > > > > + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > > + } while (err == -ERESTARTSYS);
> > > >
> > > > This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
> > > >
> > >
> > > This is caused by dma_fence_wait_timeout being unconditionally
> > > interruptible. I do not want to touch host1x stuff, this patchset
> > > already takes too much resources.
> >
> > Sure, I don't think we need to change host1x. Since this function is only called from non-user context, even if the wait is interruptible it should never be actually interrupted. So I think you can just drop the ERESTARTSYS handling.
> >
> > >
> > > > > +
> > > > > + if (err) {
> > > > > + if (err != -ERESTARTSYS)
> > > > > + dev_err_ratelimited(&chan->video.dev,
> > > > > + "frame start syncpt timeout: %d\n", err);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> > > > > + goto exit;
> > > > > + }
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > > + CSI_PP_DISABLE);
> > > > > + } else {
> > > > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > > + }
> > > > > +
> > > > > + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > > > + do {
> > > > > + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> > > > > + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > > + } while (err == -ERESTARTSYS);
> > > > >
> > > > > - /* Wait for syncpt counter to reach frame start event threshold */
> > > > > - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> > > > > - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > > > > if (err) {
> > > > > - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> > > > > - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> > > > > - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> > > > > - return err;
> > > > > + if (err != -ERESTARTSYS)
> > > > > + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> > > > > + goto exit;
> > > > > }
> > > > >
> > > > > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > > > - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > > + if (!csi_chan)
> > > > > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > > > > + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > > > >
> > > > > +exit:
> > > > > release_buffer(chan, buf, VB2_BUF_STATE_DONE);
> > > > >
> > > > > - return 0;
> > > > > + return err;
> > > > > }
> > > > >
> > > > > static int tegra20_chan_capture_kthread_start(void *data)
> > > > > {
> > > > > struct tegra_vi_channel *chan = data;
> > > > > struct tegra_channel_buffer *buf;
> > > > > + struct v4l2_subdev *csi_subdev = NULL;
> > > > > + struct tegra_csi_channel *csi_chan = NULL;
> > > > > unsigned int retries = 0;
> > > > > int err = 0;
> > > > >
> > > > > + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> > > > > + if (csi_subdev)
> > > > > + csi_chan = to_csi_chan(csi_subdev);
> > > > > +
> > > > > while (1) {
> > > > > /*
> > > > > * Source is not streaming if error is non-zero.
> > > > > @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> > > > > list_del_init(&buf->queue);
> > > > > spin_unlock(&chan->start_lock);
> > > > >
> > > > > - err = tegra20_channel_capture_frame(chan, buf);
> > > > > + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> > > > > if (!err) {
> > > > > retries = 0;
> > > > > continue;
> > > > > @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> > > > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > > > - int main_output_format;
> > > > > - int yuv_output_format;
> > > > > -
> > > > > - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> > > > > -
> > > > > - /*
> > > > > - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > > > - * for luminance, which is the default and means not to touch
> > > > > - * anything.
> > > > > - */
> > > > > - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > > > - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > > > - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > > > -
> > > > > - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > > > - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > > > > -
> > > > > - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > > - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > > - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > > - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> > > > > - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
> > > > >
> > > > > /* Set up frame size */
> > > > > tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> > > > > @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> > > > > struct media_pipeline *pipe = &chan->video.pipe;
> > > > > int err;
> > > > >
> > > > > - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > > > > -
> > > > > err = video_device_pipeline_start(&chan->video, pipe);
> > > > > if (err)
> > > > > goto error_pipeline_start;
> > > > >
> > > > > - tegra20_camera_capture_setup(chan);
> > > > > + /*
> > > > > + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > > > > + * for luminance, which is the default and means not to touch
> > > > > + * anything.
> > > > > + */
> > > > > + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > > > > + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > > > > + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > > > > +
> > > > > + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > > > > + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > > > >
> > > > > err = tegra_channel_set_stream(chan, true);
> > > > > if (err)
> > > > > goto error_set_stream;
> > > > >
> > > > > + tegra20_camera_capture_setup(chan);
> > > > > +
> > > > > chan->sequence = 0;
> > > > >
> > > > > chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> > > > > @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> > > > > .has_h_v_flip = true,
> > > > > };
> > > > >
> > > > > +/* --------------------------------------------------------------------------
> > > > > + * MIPI Calibration
> > > > > + */
> > > > > +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> > > > > +{
> > > > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > > > + unsigned int port = mipi->pads;
> > > > > + u32 value;
> > > > > + int ret;
> > > > > +
> > > > > + guard(mutex)(&csi->mipi_lock);
> > > > > +
> > > > > + ret = pm_runtime_resume_and_get(csi->dev);
> > > > > + if (ret < 0) {
> > > > > + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > +
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> > > > > + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> > > > > + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> > > > > + CSI_CIL_MIPI_CAL_TERMOS(0));
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> > > > > + CSI_PAD_DRIV_DN_REF(5) |
> > > > > + CSI_PAD_DRIV_UP_REF(7) |
> > > > > + CSI_PAD_TERM_REF(0));
> > > > > +
> > > > > + /* CSI B */
> > > > > + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > > > +
> > > > > + if (port == PORT_B)
> > > > > + value |= CSI_CIL_MIPI_CAL_SEL_B;
> > > > > +
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> > > > > +
> > > > > + /* CSI A */
> > > > > + value = CSI_CIL_MIPI_CAL_STARTCAL |
> > > > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > > > > +
> > > > > + if (port == PORT_A)
> > > > > + value |= CSI_CIL_MIPI_CAL_SEL_A;
> > > > > +
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> > > > > +
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > > > +
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> > > > > +{
> > > > > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > > > > + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> > > > > + unsigned int port = mipi->pads;
> > > > > + u32 value, pp = 0, cil = 0;
> > > > > + int ret;
> > > > > +
> > > > > + /* This part is only for CSI */
> > > > > + if (port > PORT_B) {
> > > > > + pm_runtime_put(csi->dev);
> > > > > +
> > > > > + return 0;
> > > > > + }
> > > > > +
> > > > > + guard(mutex)(&csi->mipi_lock);
> > > > > +
> > > > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > > > + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> > > > > + if (ret < 0) {
> > > > > + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> > > > > + goto exit;
> > > > > + }
> > > > > +
> > > > > + /* clear status */
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > > > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > > > > + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> > > > > + if (ret < 0) {
> > > > > + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> > > > > + goto exit;
> > > > > + }
> > > > > +
> > > > > + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > > > + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> > > > > + if (pp | cil) {
> > > > > + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> > > > > + ret = -EINVAL;
> > > > > + goto exit;
> > > > > + }
> > > > > +
> > > > > +exit:
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> > > > > +
> > > > > + /* un-select to avoid interference with DSI */
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> > > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > > > +
> > > > > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> > > > > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > > > > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > > > > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > > > > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > > > > +
> > > > > + pm_runtime_put(csi->dev);
> > > > > +
> > > > > + return ret;
> > > > > +}
> > > > > +
> > > > > +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> > > > > + .start_calibration = tegra20_start_pad_calibration,
> > > > > + .finish_calibration = tegra20_finish_pad_calibration,
> > > > > +};
> > > > > +
> > > > > +/* --------------------------------------------------------------------------
> > > > > + * CSI
> > > > > + */
> > > > > +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> > > > > +{
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> > > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> > > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> > > > > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> > > > > +}
> > > > > +
> > > > > +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> > > > > + u8 portno)
> > > > > +{
> > > > > + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> > > > > + int width = vi_chan->format.width;
> > > > > + int height = vi_chan->format.height;
> > > > > + u32 data_type = vi_chan->fmtinfo->img_dt;
> > > > > + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> > > > > + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> > > > > +
> > > > > + unsigned int main_output_format, yuv_output_format;
> > > > > + unsigned int port = portno & 1;
> > > > > + u32 value;
> > > > > +
> > > > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > > > > +
> > > > > + switch (data_type) {
> > > > > + case TEGRA_IMAGE_DT_RAW8:
> > > > > + case TEGRA_IMAGE_DT_RAW10:
> > > > > + output_channel = TEGRA_VI_OUT_2;
> > > > > + if (port == PORT_A)
> > > > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> > > > > + else
> > > > > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> > > > > + break;
> > > > > + }
> > > > > +
> > > > > + tegra20_csi_capture_clean(csi_chan);
> > > > > +
> > > > > + /* CSI port cleanup */
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> > > > > + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> > > > > + CSI_PP_EXP_FRAME_HEIGHT(height) |
> > > > > + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> > > > > + CSI_PP_LINE_TIMEOUT_ENABLE);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> > > > > + CSI_PP_OUTPUT_FORMAT_PIXEL |
> > > > > + CSI_PP_DATA_TYPE(data_type) |
> > > > > + CSI_PP_CRC_CHECK_ENABLE |
> > > > > + CSI_PP_WORD_COUNT_HEADER |
> > > > > + CSI_PP_DATA_IDENTIFIER_ENABLE |
> > > > > + CSI_PP_PACKET_HEADER_SENT |
> > > > > + port);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> > > > > + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> > > > > + (csi_chan->numlanes - 1));
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> > > > > + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> > > > > + 0x5); /* Clock settle time */
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> > > > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > > > + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> > > > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> > > > > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > > > > + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> > > > > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > > > > +
> > > > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> > > > > + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > > + CSI_PP_DISABLE);
> > > > > +
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > > + yuv_output_format | main_output_format);
> > > > > +
> > > > > + return 0;
> > > > > +};
> > > > > +
> > > > > +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> > > > > +{
> > > > > + struct tegra_csi *csi = csi_chan->csi;
> > > > > + unsigned int port = portno & 1;
> > > > > + u32 value;
> > > > > +
> > > > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > > > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> > > > > +
> > > > > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> > > > > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> > > > > +
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > > > > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > > > > + CSI_PP_DISABLE);
> > > > > +
> > > > > + if (csi_chan->numlanes == 4) {
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> > > > > + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> > > > > + } else {
> > > > > + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> > > > > + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> > > > > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> > > > > +{
> > > > > + u8 *portnos = csi_chan->csi_port_nums;
> > > > > + int ret, i;
> > > > > +
> > > > > + for (i = 0; i < csi_chan->numgangports; i++) {
> > > > > + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> > > > > + if (ret)
> > > > > + goto stream_start_fail;
> > > > > + }
> > > > > +
> > > > > + return 0;
> > > > > +
> > > > > +stream_start_fail:
> > > > > + for (i = i - 1; i >= 0; i--)
> > > > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > > > +
> > > > > + return ret;
> > > > > +}
> > > > > +
> > > > > +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> > > > > +{
> > > > > + u8 *portnos = csi_chan->csi_port_nums;
> > > > > + int i;
> > > > > +
> > > > > + for (i = 0; i < csi_chan->numgangports; i++)
> > > > > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > > > > +}
> > > > > +
> > > > > +static const struct tegra_csi_ops tegra20_csi_ops = {
> > > > > + .csi_start_streaming = tegra20_csi_start_streaming,
> > > > > + .csi_stop_streaming = tegra20_csi_stop_streaming,
> > > > > +};
> > > > > +
> > > > > +static const char * const tegra20_csi_clks[] = {
> > > > > + NULL,
> > > > > +};
> > > > > +
> > > > > +const struct tegra_csi_soc tegra20_csi_soc = {
> > > > > + .ops = &tegra20_csi_ops,
> > > > > + .mipi_ops = &tegra20_mipi_ops,
> > > > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > > > + .clk_names = tegra20_csi_clks,
> > > > > + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> > > > > +};
> > > > > +
> > > > > +static const char * const tegra30_csi_clks[] = {
> > > > > + "csi",
> > > > > + "csia-pad",
> > > > > + "csib-pad",
> > > > > +};
> > > > > +
> > > > > +const struct tegra_csi_soc tegra30_csi_soc = {
> > > > > + .ops = &tegra20_csi_ops,
> > > > > + .mipi_ops = &tegra20_mipi_ops,
> > > > > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > > > > + .clk_names = tegra30_csi_clks,
> > > > > + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> > > > > +};
> > > > > +
> > > > > /* --------------------------------------------------------------------------
> > > > > * VIP
> > > > > */
> > > > > @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > > > > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > > > > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > > > > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > > > > - unsigned int main_input_format;
> > > > > - unsigned int yuv_input_format;
> > > > > + unsigned int main_input_format, yuv_input_format;
> > > > > + unsigned int main_output_format, yuv_output_format;
> > > > >
> > > > > tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> > > > > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > > > >
> > > > > tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
> > > > >
> > > > > @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > > > >
> > > > > tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
> > > > >
> > > > > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > > > > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > > > > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > > > > + yuv_output_format | main_output_format);
> > > > > +
> > > > > return 0;
> > > > > }
> > > > >
> > > > > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > > > > index 5cbc0606ed6c..bad55e0bd313 100644
> > > > > --- a/drivers/staging/media/tegra-video/vi.h
> > > > > +++ b/drivers/staging/media/tegra-video/vi.h
> > > > > @@ -125,7 +125,6 @@ struct tegra_vi {
> > > > > * frame through host1x syncpoint counters (On Tegra20 used for the
> > > > > * OUT_1 syncpt)
> > > > > * @sp_incr_lock: protects cpu syncpoint increment.
> > > > > - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> > > > > *
> > > > > * @kthread_start_capture: kthread to start capture of single frame when
> > > > > * vb buffer is available. This thread programs VI CSI hardware
> > > > > @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> > > > > struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> > > > > /* protects the cpu syncpoint increment */
> > > > > spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> > > > > - u32 next_out_sp_idx;
> > > > >
> > > > > struct task_struct *kthread_start_capture;
> > > > > wait_queue_head_t start_wait;
> > > > > diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> > > > > index 6fe8d5301b9c..9f2bddc460bf 100644
> > > > > --- a/drivers/staging/media/tegra-video/video.c
> > > > > +++ b/drivers/staging/media/tegra-video/video.c
> > > > > @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> > > > > { .compatible = "nvidia,tegra20-vip", },
> > > > > { .compatible = "nvidia,tegra20-vi", },
> > > > > #endif
> > > > > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > > > > + { .compatible = "nvidia,tegra20-csi", },
> > > > > +#endif
> > > > > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > > > > + { .compatible = "nvidia,tegra30-csi", },
> > > > > +#endif
> > > > > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > > > > { .compatible = "nvidia,tegra210-csi", },
> > > > > { .compatible = "nvidia,tegra210-vi", },
> > > > >
> > > >
> > > >
> > > >
> > > >
> >
> >
> >
> >
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
2025-10-02 1:52 ` Rob Herring
@ 2025-10-02 5:14 ` Svyatoslav Ryhel
2025-10-06 20:31 ` Rob Herring
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-02 5:14 UTC (permalink / raw)
To: Rob Herring
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann, dri-devel,
devicetree, linux-tegra, linux-kernel, linux-media, linux-clk,
linux-gpio, linux-staging
чт, 2 жовт. 2025 р. о 04:52 Rob Herring <robh@kernel.org> пише:
>
> On Thu, Sep 25, 2025 at 06:16:46PM +0300, Svyatoslav Ryhel wrote:
> > Document CSI HW block found in Tegra20 and Tegra30 SoC.
> >
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> > .../display/tegra/nvidia,tegra20-csi.yaml | 135 ++++++++++++++++++
> > 1 file changed, 135 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > new file mode 100644
> > index 000000000000..817b3097846b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > @@ -0,0 +1,135 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-csi.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: NVIDIA Tegra20 CSI controller
> > +
> > +maintainers:
> > + - Svyatoslav Ryhel <clamor95@gmail.com>
> > +
> > +properties:
> > + compatible:
> > + enum:
> > + - nvidia,tegra20-csi
> > + - nvidia,tegra30-csi
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + clocks: true
> > + clock-names: true
> > +
> > + avdd-dsi-csi-supply:
> > + description: DSI/CSI power supply. Must supply 1.2 V.
> > +
> > + power-domains:
> > + maxItems: 1
> > +
> > + "#nvidia,mipi-calibrate-cells":
> > + description:
> > + The number of cells in a MIPI calibration specifier. Should be 1.
> > + The single cell specifies an id of the pad that need to be
> > + calibrated for a given device. Valid pad ids for receiver would be
> > + 0 for CSI-A; 1 for CSI-B; 2 for DSI-A and 3 for DSI-B.
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > + const: 1
>
> Sorry I didn't bring this up before, but is this ever not 1? If it is
> fixed, then you don't really need the property. I prefer it just be
> fixed rather than getting a bunch of vendor specific #foo-cells.
>
This is not an introduction of property, such property already exists
in Documentation/devicetree/bindings/display/tegra/nvidia,tegra114-mipi.yaml
and is used in multiple device trees. As I have told before, in case
of Tegra30 and Tegra20 CSI block combines mipi calibration function
and CSI function, in Tegra114+ mipi calibration got a dedicated
hardware block which is already supported. This property here is used
to align with mipi-calibration logic used by Tegra114+
>
> > +
> > + "#address-cells":
> > + const: 1
> > +
> > + "#size-cells":
> > + const: 0
> > +
> > +patternProperties:
> > + "^channel@[0-1]$":
> > + type: object
> > + description: channel 0 represents CSI-A and 1 represents CSI-B
> > + additionalProperties: false
> > +
> > + properties:
> > + reg:
> > + maximum: 1
> > +
> > + nvidia,mipi-calibrate:
> > + description: Should contain a phandle and a specifier specifying
> > + which pad is used by this CSI channel and needs to be calibrated.
> > + $ref: /schemas/types.yaml#/definitions/phandle-array
>
> Sounds like only one entry? Then 'maxItems: 1' is needed. If you drop
> #nvidia,mipi-calibrate-cells, then you need to define the arg size too:
>
> items:
> - items:
> - description: phandle to ...
> - description: what the arg contains.
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-02 4:00 ` Mikko Perttunen
@ 2025-10-02 5:41 ` Svyatoslav Ryhel
2025-10-02 6:12 ` Mikko Perttunen
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-02 5:41 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
чт, 2 жовт. 2025 р. о 07:00 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Wednesday, October 1, 2025 4:59 PM Svyatoslav Ryhel wrote:
> > ср, 1 жовт. 2025 р. о 10:51 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > >
> > > On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> > > > ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> > > > >
> > > > > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > > > >
> > > > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > > > Simplify format align calculations by slightly modifying supported formats
> > > > > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > > > > width since each plain has stride width * 1. This aligns with downstream
> > > > > >
> > > > > > plane
> > > > > >
> > > > > > > behavior which uses same approach for offset calculations.
> > > > > > >
> > > > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > > > ---
> > > > > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > > > > >
> > > > > > > - switch (pix->pixelformat) {
> > > > > > > - case V4L2_PIX_FMT_UYVY:
> > > > > > > - case V4L2_PIX_FMT_VYUY:
> > > > > > > - case V4L2_PIX_FMT_YUYV:
> > > > > > > - case V4L2_PIX_FMT_YVYU:
> > > > > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > > > > - break;
> > > > > > > - case V4L2_PIX_FMT_YUV420:
> > > > > > > - case V4L2_PIX_FMT_YVU420:
> > > > > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > > > > - break;
> > > > > > > - }
> > > > > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > > > > }
> > > > > > >
> > > > > > > /*
> > > > > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > {
> > > > > > > unsigned int stride = chan->format.bytesperline;
> > > > > > > unsigned int height = chan->format.height;
> > > > > > > + unsigned int width = chan->format.width;
> > > > > > >
> > > > > > > chan->start_offset = 0;
> > > > > > >
> > > > > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > >
> > > > > > > case V4L2_PIX_FMT_YUV420:
> > > > > > > case V4L2_PIX_FMT_YVU420:
> > > > > > > - chan->addr_offset_u = stride * height;
> > > > > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > > > > + chan->addr_offset_u = width * height;
> > > > > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > > > > >
> > > > > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > chan->start_offset_v = chan->addr_offset_v;
> > > > > > >
> > > > > > > if (chan->vflip) {
> > > > > > > - chan->start_offset += stride * (height - 1);
> > > > > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > > > > + chan->start_offset += width * (height - 1);
> > > > > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > > > > }
> > > > > > > if (chan->hflip) {
> > > > > > > - chan->start_offset += stride - 1;
> > > > > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > > > > + chan->start_offset += width - 1;
> > > > > > > + chan->start_offset_u += (width / 2) - 1;
> > > > > > > + chan->start_offset_v += (width / 2) - 1;
> > > > > > > }
> > > > > > > break;
> > > > > > > }
> > > > > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > > > > };
> > > > > > >
> > > > > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > > > > -{ \
> > > > > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > - .bpp = BPP, \
> > > > > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > > > > +{ \
> > > > > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > > > > + .bit_width = BIT_WIDTH, \
> > > > > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > + .bpp = BPP, \
> > > > > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > }
> > > > > > >
> > > > > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > > > > + /* YUV422 */
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > > > > };
> > > > > >
> > > > > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > > > > >
> > > > > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > > > > >
> > > > >
> > > > > No, this code is actually cleaner and in sync with what downstream
> > > > > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > > > > formats with fractional bytes per pixel, it is impossible to set there
> > > > > 3/2 for example, which is used by YUV420.
> > > > >
> > > > > According to downstream code bytes_per_line =
> > > > > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > > > > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > > > > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > > > > different buffers so with offset calculation plain width must be used
> > > > > (which matches downstream).
> > > > >
> > > >
> > > > If you mean use of BPP by VI, I can propose removing bytesperline and
> > > > sizeimage configuration from VI entirely and leave this to per-SoC
> > > > fmt_align function which does this already anyway and guards every
> > > > time those values are referred. This way there will be no instances
> > > > where "places not being the actual bytes per line" comes true.
> > >
> > > Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
> > >
> >
> > I am proposing you a solution, handle bytesperline and sizeimage in
> > per-SoC fmt_align function.
>
> Ok, I think that sounds good. It's good to consolidate the calculation in one place.
>
> >
> > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> > plane and 2 for V plane, total is 12. "but explainable with a comment
> > and improve-able later" why then we cannot use 12 with a comment? this
> > is all arbitrary. Downstream is not wrong from this perspective, you
> > don't take into account that YUV420 is planar and it uses 3 planes a
> > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> > 1/4 width which is width * 3/2
>
> Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
>
https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-02 5:41 ` Svyatoslav Ryhel
@ 2025-10-02 6:12 ` Mikko Perttunen
2025-10-02 6:20 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Mikko Perttunen @ 2025-10-02 6:12 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
On Thursday, October 2, 2025 2:41 PM Svyatoslav Ryhel wrote:
> чт, 2 жовт. 2025 р. о 07:00 Mikko Perttunen <mperttunen@nvidia.com> пише:
> >
> > On Wednesday, October 1, 2025 4:59 PM Svyatoslav Ryhel wrote:
> > > ср, 1 жовт. 2025 р. о 10:51 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > >
> > > > On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> > > > > ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> > > > > >
> > > > > > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > > > > >
> > > > > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > > > > Simplify format align calculations by slightly modifying supported formats
> > > > > > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > > > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > > > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > > > > > width since each plain has stride width * 1. This aligns with downstream
> > > > > > >
> > > > > > > plane
> > > > > > >
> > > > > > > > behavior which uses same approach for offset calculations.
> > > > > > > >
> > > > > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > > > > ---
> > > > > > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > > > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > > > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > > > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > > > > > >
> > > > > > > > - switch (pix->pixelformat) {
> > > > > > > > - case V4L2_PIX_FMT_UYVY:
> > > > > > > > - case V4L2_PIX_FMT_VYUY:
> > > > > > > > - case V4L2_PIX_FMT_YUYV:
> > > > > > > > - case V4L2_PIX_FMT_YVYU:
> > > > > > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > > > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > > > > > - break;
> > > > > > > > - case V4L2_PIX_FMT_YUV420:
> > > > > > > > - case V4L2_PIX_FMT_YVU420:
> > > > > > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > > > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > > > > > - break;
> > > > > > > > - }
> > > > > > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > > > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > > > > > }
> > > > > > > >
> > > > > > > > /*
> > > > > > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > > {
> > > > > > > > unsigned int stride = chan->format.bytesperline;
> > > > > > > > unsigned int height = chan->format.height;
> > > > > > > > + unsigned int width = chan->format.width;
> > > > > > > >
> > > > > > > > chan->start_offset = 0;
> > > > > > > >
> > > > > > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > >
> > > > > > > > case V4L2_PIX_FMT_YUV420:
> > > > > > > > case V4L2_PIX_FMT_YVU420:
> > > > > > > > - chan->addr_offset_u = stride * height;
> > > > > > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > > > > > + chan->addr_offset_u = width * height;
> > > > > > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > > > > > >
> > > > > > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > > > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > > > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > > chan->start_offset_v = chan->addr_offset_v;
> > > > > > > >
> > > > > > > > if (chan->vflip) {
> > > > > > > > - chan->start_offset += stride * (height - 1);
> > > > > > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > > > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > > > > > + chan->start_offset += width * (height - 1);
> > > > > > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > > > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > > > > > }
> > > > > > > > if (chan->hflip) {
> > > > > > > > - chan->start_offset += stride - 1;
> > > > > > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > > > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > > > > > + chan->start_offset += width - 1;
> > > > > > > > + chan->start_offset_u += (width / 2) - 1;
> > > > > > > > + chan->start_offset_v += (width / 2) - 1;
> > > > > > > > }
> > > > > > > > break;
> > > > > > > > }
> > > > > > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > > > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > > > > > };
> > > > > > > >
> > > > > > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > > > > > -{ \
> > > > > > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > > - .bpp = BPP, \
> > > > > > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > > > > > +{ \
> > > > > > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > > > > > + .bit_width = BIT_WIDTH, \
> > > > > > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > > + .bpp = BPP, \
> > > > > > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > > }
> > > > > > > >
> > > > > > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > > > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > > > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > > > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > > > > > + /* YUV422 */
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > > > > > };
> > > > > > >
> > > > > > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > > > > > >
> > > > > > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > > > > > >
> > > > > >
> > > > > > No, this code is actually cleaner and in sync with what downstream
> > > > > > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > > > > > formats with fractional bytes per pixel, it is impossible to set there
> > > > > > 3/2 for example, which is used by YUV420.
> > > > > >
> > > > > > According to downstream code bytes_per_line =
> > > > > > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > > > > > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > > > > > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > > > > > different buffers so with offset calculation plain width must be used
> > > > > > (which matches downstream).
> > > > > >
> > > > >
> > > > > If you mean use of BPP by VI, I can propose removing bytesperline and
> > > > > sizeimage configuration from VI entirely and leave this to per-SoC
> > > > > fmt_align function which does this already anyway and guards every
> > > > > time those values are referred. This way there will be no instances
> > > > > where "places not being the actual bytes per line" comes true.
> > > >
> > > > Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
> > > >
> > >
> > > I am proposing you a solution, handle bytesperline and sizeimage in
> > > per-SoC fmt_align function.
> >
> > Ok, I think that sounds good. It's good to consolidate the calculation in one place.
> >
> > >
> > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> > > plane and 2 for V plane, total is 12. "but explainable with a comment
> > > and improve-able later" why then we cannot use 12 with a comment? this
> > > is all arbitrary. Downstream is not wrong from this perspective, you
> > > don't take into account that YUV420 is planar and it uses 3 planes a
> > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> > > 1/4 width which is width * 3/2
> >
> > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
> >
> https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-02 6:12 ` Mikko Perttunen
@ 2025-10-02 6:20 ` Svyatoslav Ryhel
2025-10-06 18:54 ` Luca Ceresoli
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-02 6:20 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
чт, 2 жовт. 2025 р. о 09:12 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Thursday, October 2, 2025 2:41 PM Svyatoslav Ryhel wrote:
> > чт, 2 жовт. 2025 р. о 07:00 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > >
> > > On Wednesday, October 1, 2025 4:59 PM Svyatoslav Ryhel wrote:
> > > > ср, 1 жовт. 2025 р. о 10:51 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > > >
> > > > > On Wednesday, October 1, 2025 2:35 PM Svyatoslav Ryhel wrote:
> > > > > > ср, 1 жовт. 2025 р. о 08:07 Svyatoslav Ryhel <clamor95@gmail.com> пише:
> > > > > > >
> > > > > > > ср, 1 жовт. 2025 р. о 07:38 Mikko Perttunen <mperttunen@nvidia.com> пише:
> > > > > > > >
> > > > > > > > On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > > > > > > > > Simplify format align calculations by slightly modifying supported formats
> > > > > > > > > structure. Adjusted U and V offset calculations for planar formats since
> > > > > > > > > YUV420P bits per pixel is 12 (1 full plane for Y + 2 * 1/4 planes for U
> > > > > > > > > and V) so stride is width * 3/2, but offset must be calculated with plain
> > > > > > > > > width since each plain has stride width * 1. This aligns with downstream
> > > > > > > >
> > > > > > > > plane
> > > > > > > >
> > > > > > > > > behavior which uses same approach for offset calculations.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > > > > > > ---
> > > > > > > > > drivers/staging/media/tegra-video/tegra20.c | 58 +++++++++------------
> > > > > > > > > drivers/staging/media/tegra-video/vi.h | 3 +-
> > > > > > > > > 2 files changed, 27 insertions(+), 34 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > > index 7c3ff843235d..b7a39723dfc2 100644
> > > > > > > > > --- a/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > > > > > > > > @@ -280,20 +280,8 @@ static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > > > > > > > > pix->width = clamp(pix->width, TEGRA20_MIN_WIDTH, TEGRA20_MAX_WIDTH);
> > > > > > > > > pix->height = clamp(pix->height, TEGRA20_MIN_HEIGHT, TEGRA20_MAX_HEIGHT);
> > > > > > > > >
> > > > > > > > > - switch (pix->pixelformat) {
> > > > > > > > > - case V4L2_PIX_FMT_UYVY:
> > > > > > > > > - case V4L2_PIX_FMT_VYUY:
> > > > > > > > > - case V4L2_PIX_FMT_YUYV:
> > > > > > > > > - case V4L2_PIX_FMT_YVYU:
> > > > > > > > > - pix->bytesperline = roundup(pix->width, 2) * 2;
> > > > > > > > > - pix->sizeimage = roundup(pix->width, 2) * 2 * pix->height;
> > > > > > > > > - break;
> > > > > > > > > - case V4L2_PIX_FMT_YUV420:
> > > > > > > > > - case V4L2_PIX_FMT_YVU420:
> > > > > > > > > - pix->bytesperline = roundup(pix->width, 8);
> > > > > > > > > - pix->sizeimage = roundup(pix->width, 8) * pix->height * 3 / 2;
> > > > > > > > > - break;
> > > > > > > > > - }
> > > > > > > > > + pix->bytesperline = DIV_ROUND_UP(pix->width * bpp, 8);
> > > > > > > > > + pix->sizeimage = pix->bytesperline * pix->height;
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > /*
> > > > > > > > > @@ -305,6 +293,7 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > > > {
> > > > > > > > > unsigned int stride = chan->format.bytesperline;
> > > > > > > > > unsigned int height = chan->format.height;
> > > > > > > > > + unsigned int width = chan->format.width;
> > > > > > > > >
> > > > > > > > > chan->start_offset = 0;
> > > > > > > > >
> > > > > > > > > @@ -321,8 +310,8 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > > >
> > > > > > > > > case V4L2_PIX_FMT_YUV420:
> > > > > > > > > case V4L2_PIX_FMT_YVU420:
> > > > > > > > > - chan->addr_offset_u = stride * height;
> > > > > > > > > - chan->addr_offset_v = chan->addr_offset_u + stride * height / 4;
> > > > > > > > > + chan->addr_offset_u = width * height;
> > > > > > > > > + chan->addr_offset_v = chan->addr_offset_u + width * height / 4;
> > > > > > > > >
> > > > > > > > > /* For YVU420, we swap the locations of the U and V planes. */
> > > > > > > > > if (chan->format.pixelformat == V4L2_PIX_FMT_YVU420)
> > > > > > > > > @@ -332,14 +321,14 @@ static void tegra20_channel_queue_setup(struct tegra_vi_channel *chan)
> > > > > > > > > chan->start_offset_v = chan->addr_offset_v;
> > > > > > > > >
> > > > > > > > > if (chan->vflip) {
> > > > > > > > > - chan->start_offset += stride * (height - 1);
> > > > > > > > > - chan->start_offset_u += (stride / 2) * ((height / 2) - 1);
> > > > > > > > > - chan->start_offset_v += (stride / 2) * ((height / 2) - 1);
> > > > > > > > > + chan->start_offset += width * (height - 1);
> > > > > > > > > + chan->start_offset_u += (width / 2) * ((height / 2) - 1);
> > > > > > > > > + chan->start_offset_v += (width / 2) * ((height / 2) - 1);
> > > > > > > > > }
> > > > > > > > > if (chan->hflip) {
> > > > > > > > > - chan->start_offset += stride - 1;
> > > > > > > > > - chan->start_offset_u += (stride / 2) - 1;
> > > > > > > > > - chan->start_offset_v += (stride / 2) - 1;
> > > > > > > > > + chan->start_offset += width - 1;
> > > > > > > > > + chan->start_offset_u += (width / 2) - 1;
> > > > > > > > > + chan->start_offset_v += (width / 2) - 1;
> > > > > > > > > }
> > > > > > > > > break;
> > > > > > > > > }
> > > > > > > > > @@ -576,20 +565,23 @@ static const struct tegra_vi_ops tegra20_vi_ops = {
> > > > > > > > > .vi_stop_streaming = tegra20_vi_stop_streaming,
> > > > > > > > > };
> > > > > > > > >
> > > > > > > > > -#define TEGRA20_VIDEO_FMT(MBUS_CODE, BPP, FOURCC) \
> > > > > > > > > -{ \
> > > > > > > > > - .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > > > - .bpp = BPP, \
> > > > > > > > > - .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > > > +#define TEGRA20_VIDEO_FMT(DATA_TYPE, BIT_WIDTH, MBUS_CODE, BPP, FOURCC) \
> > > > > > > > > +{ \
> > > > > > > > > + .img_dt = TEGRA_IMAGE_DT_##DATA_TYPE, \
> > > > > > > > > + .bit_width = BIT_WIDTH, \
> > > > > > > > > + .code = MEDIA_BUS_FMT_##MBUS_CODE, \
> > > > > > > > > + .bpp = BPP, \
> > > > > > > > > + .fourcc = V4L2_PIX_FMT_##FOURCC, \
> > > > > > > > > }
> > > > > > > > >
> > > > > > > > > static const struct tegra_video_format tegra20_video_formats[] = {
> > > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 2, UYVY),
> > > > > > > > > - TEGRA20_VIDEO_FMT(VYUY8_2X8, 2, VYUY),
> > > > > > > > > - TEGRA20_VIDEO_FMT(YUYV8_2X8, 2, YUYV),
> > > > > > > > > - TEGRA20_VIDEO_FMT(YVYU8_2X8, 2, YVYU),
> > > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YUV420),
> > > > > > > > > - TEGRA20_VIDEO_FMT(UYVY8_2X8, 1, YVU420),
> > > > > > > > > + /* YUV422 */
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 16, UYVY),
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, VYUY8_2X8, 16, VYUY),
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YUYV8_2X8, 16, YUYV),
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, YVYU8_2X8, 16, YVYU),
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YUV420),
> > > > > > > > > + TEGRA20_VIDEO_FMT(YUV422_8, 16, UYVY8_2X8, 12, YVU420),
> > > > > > > > > };
> > > > > > > >
> > > > > > > > Looking at the code, BPP seems to only be used for the line stride (i.e. bytes per line) calculation. I think we should just make it 8 for the planar formats (possibly with an explaining comment). With the current code, we end up with 'bytesperline' variables in places not being the actual bytes per line, which is confusing.
> > > > > > > >
> > > > > > > > Actually, we can then just make the 'bpp' field be bytes per pixel as it was before to avoid the discrepancy with Tegra210.
> > > > > > > >
> > > > > > >
> > > > > > > No, this code is actually cleaner and in sync with what downstream
> > > > > > > does, Tegra210 bytes per pixel is confusing since it totally neglects
> > > > > > > formats with fractional bytes per pixel, it is impossible to set there
> > > > > > > 3/2 for example, which is used by YUV420.
> > > > > > >
> > > > > > > According to downstream code bytes_per_line =
> > > > > > > soc_mbus_bytes_per_line..., downstream directly name is bytes_per_line
> > > > > > > and soc_mbus_bytes_per_line returns width * 3 / 2 which is correct
> > > > > > > calculation (12 bits). Meanwhile for planar formats Tegra has 3
> > > > > > > different buffers so with offset calculation plain width must be used
> > > > > > > (which matches downstream).
> > > > > > >
> > > > > >
> > > > > > If you mean use of BPP by VI, I can propose removing bytesperline and
> > > > > > sizeimage configuration from VI entirely and leave this to per-SoC
> > > > > > fmt_align function which does this already anyway and guards every
> > > > > > time those values are referred. This way there will be no instances
> > > > > > where "places not being the actual bytes per line" comes true.
> > > > >
> > > > > Without trying myself, I'm not sure what approach is the cleanest. In any case, the downstream code is just wrong (or incorrectly named), so we shouldn't defer to it in this matter. I don't see a reason to keep the value '12' either if it doesn't serve any purpose (admittedly if we changed it to 8 or 1, 'bpp' would be a confusing name for it, but explainable with a comment and improve-able later) I don't mind having an if/switch statement for the planar formats to use a '8' as multiplier instead of '12' if we need to keep the '12'. But the main thing I want to avoid is a bytesperline/stride variable that isn't the line stride in bytes.
> > > > >
> > > >
> > > > I am proposing you a solution, handle bytesperline and sizeimage in
> > > > per-SoC fmt_align function.
> > >
> > > Ok, I think that sounds good. It's good to consolidate the calculation in one place.
> > >
> > > >
> > > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> > > > plane and 2 for V plane, total is 12. "but explainable with a comment
> > > > and improve-able later" why then we cannot use 12 with a comment? this
> > > > is all arbitrary. Downstream is not wrong from this perspective, you
> > > > don't take into account that YUV420 is planar and it uses 3 planes a
> > > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> > > > 1/4 width which is width * 3/2
> > >
> > > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
> > >
> > https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
>
> I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
No, it has direct impact. This is how buffer size / image size is
calculated since we place each plane consecutive. And bytes per line
is used specifically in image size calculation. This is common part
with non-planar formats. Then since Tegra provides a dedicated
channels/buffers for each plane, configuration of planar format
includes an additional step with calculation for each plane.
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 22/22] staging: media: tegra-video: add CSI support for Tegra20 and Tegra30
2025-10-01 5:04 ` Mikko Perttunen
2025-10-01 5:15 ` Svyatoslav Ryhel
@ 2025-10-02 17:49 ` Svyatoslav Ryhel
1 sibling, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-02 17:49 UTC (permalink / raw)
To: Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Linus Walleij, Mauro Carvalho Chehab, Greg Kroah-Hartman,
Jonas Schwöbel, Dmitry Osipenko, Charan Pedumuru, Diogo Ivo,
Aaron Kling, Arnd Bergmann, dri-devel, devicetree, linux-tegra,
linux-kernel, linux-media, linux-clk, linux-gpio, linux-staging
ср, 1 жовт. 2025 р. о 08:04 Mikko Perttunen <mperttunen@nvidia.com> пише:
>
> On Friday, September 26, 2025 12:16 AM Svyatoslav Ryhel wrote:
> > Add support for MIPI CSI device and calibration logic found in Tegra20 and
> > Tegra30 SoC.
> >
> > To get CSI operational, an additional syncpoint was allocated to serve as
> > the CSI frame counter. Both VIP and CSI use an existing syncpoint for VI
> > frame start events. That said, the frame capture function was refactored
> > to reflect the addition of the CSI syncpoint, and the CSI-specific
> > configuration is guarded by the presence of a passed CSI channel structure
> > pointer.
> >
> > The camera capture setup's configuration was reconsidered: the first two
> > writes must be done before tegra_channel_set_stream for MIPI calibration
> > to work properly; the third write was moved to VIP/CSI-specific functions
> > since it must be source-specific; the function was placed after
> > tegra_channel_set_stream so the initial sequence is preserved and expanded.
> >
> > CSI configuration sequences were added based on downstream 3.1 kernel
> > sources and adjusted to the existing video-tegra framework. Although
> > Tegra20 and Tegra30 have the same set of configurations, they differ by
> > the number of clocks used by CSI.
> >
> > Dropped the software syncpoint counters in favor of reading syncpoints
> > directly and passing the incremented value to the polling function. If the
> > syncpoint increase fails, the PP is reset. This change should prevent
> > possible race conditions.
> >
> > MIPI calibration logic was registered in CSI since Tegra20 and Tegra30
> > have no dedicated hardware block for these operations and use CSI. These
> > calls are used for both CSI and DSI to work properly, which is why MIPI
> > calibration cannot be contained within CSI. The pads passed to the
> > calibration calls resemble CSI PORT_A (0), CSI PORT_B (1), DSI-A (3) and
> > DSI-B (4).
> >
> > Co-developed-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > ---
> > drivers/staging/media/tegra-video/csi.c | 19 +
> > drivers/staging/media/tegra-video/csi.h | 4 +
> > drivers/staging/media/tegra-video/tegra20.c | 608 ++++++++++++++++++--
> > drivers/staging/media/tegra-video/vi.h | 2 -
> > drivers/staging/media/tegra-video/video.c | 6 +
> > 5 files changed, 592 insertions(+), 47 deletions(-)
> >
> > diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
> > index 7d70478a07aa..92ee4c84a988 100644
> > --- a/drivers/staging/media/tegra-video/csi.c
> > +++ b/drivers/staging/media/tegra-video/csi.c
> > @@ -827,6 +827,13 @@ static int tegra_csi_probe(struct platform_device *pdev)
> >
> > csi->dev = &pdev->dev;
> > csi->ops = csi->soc->ops;
> > +
> > + if (csi->soc->mipi_ops)
> > + tegra_mipi_add_provider(pdev->dev.of_node,
> > + csi->soc->mipi_ops);
>
> Error handling should be added. Also, I realize that we should have a tegra_mipi_remove_provider to call if the probe fails after this or at CSI device removal. Since tegra_mipi_request refcounts the platform device, AIUI the CSI device cannot be unbound while it has users, so we don't need to worry about the CSI device being removed while there are active users.
>
> > +
> > + mutex_init(&csi->mipi_lock);
> > +
> > platform_set_drvdata(pdev, csi);
> > pm_runtime_enable(&pdev->dev);
> >
> > @@ -858,11 +865,23 @@ static void tegra_csi_remove(struct platform_device *pdev)
> > pm_runtime_disable(&pdev->dev);
> > }
> >
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > +extern const struct tegra_csi_soc tegra20_csi_soc;
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > +extern const struct tegra_csi_soc tegra30_csi_soc;
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > extern const struct tegra_csi_soc tegra210_csi_soc;
> > #endif
> >
> > static const struct of_device_id tegra_csi_of_id_table[] = {
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > + { .compatible = "nvidia,tegra20-csi", .data = &tegra20_csi_soc },
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > + { .compatible = "nvidia,tegra30-csi", .data = &tegra30_csi_soc },
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > { .compatible = "nvidia,tegra210-csi", .data = &tegra210_csi_soc },
> > #endif
> > diff --git a/drivers/staging/media/tegra-video/csi.h b/drivers/staging/media/tegra-video/csi.h
> > index 1550defb115a..422f30655945 100644
> > --- a/drivers/staging/media/tegra-video/csi.h
> > +++ b/drivers/staging/media/tegra-video/csi.h
> > @@ -115,6 +115,7 @@ struct tegra_csi_ops {
> > * struct tegra_csi_soc - NVIDIA Tegra CSI SoC structure
> > *
> > * @ops: csi hardware operations
> > + * @mipi_ops: MIPI calibration operations
> > * @csi_max_channels: supported max streaming channels
> > * @clk_names: csi and cil clock names
> > * @num_clks: total clocks count
> > @@ -123,6 +124,7 @@ struct tegra_csi_ops {
> > */
> > struct tegra_csi_soc {
> > const struct tegra_csi_ops *ops;
> > + const struct tegra_mipi_ops *mipi_ops;
> > unsigned int csi_max_channels;
> > const char * const *clk_names;
> > unsigned int num_clks;
> > @@ -140,6 +142,7 @@ struct tegra_csi_soc {
> > * @vdd: vdd regulator for CSI hardware, usually avdd_dsi_csi
> > * @soc: pointer to SoC data structure
> > * @ops: csi operations
> > + * @mipi_lock: for MIPI calibration operations
> > * @csi_chans: list head for CSI channels
> > */
> > struct tegra_csi {
> > @@ -150,6 +153,7 @@ struct tegra_csi {
> > struct regulator *vdd;
> > const struct tegra_csi_soc *soc;
> > const struct tegra_csi_ops *ops;
> > + struct mutex mipi_lock;
> > struct list_head csi_chans;
> > };
> >
> > diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c
> > index 8c9655ffa886..d99a04fa25af 100644
> > --- a/drivers/staging/media/tegra-video/tegra20.c
> > +++ b/drivers/staging/media/tegra-video/tegra20.c
> > @@ -4,6 +4,9 @@
> > *
> > * Copyright (C) 2023 SKIDATA GmbH
> > * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
> > + *
> > + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
> > + * Copyright (c) 2025 Jonas Schwöbel <jonasschwoebel@yahoo.de>
> > */
> >
> > /*
> > @@ -12,10 +15,15 @@
> > */
> >
> > #include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/clk/tegra.h>
> > #include <linux/delay.h>
> > #include <linux/host1x.h>
> > +#include <linux/iopoll.h>
> > #include <linux/kernel.h>
> > #include <linux/kthread.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/tegra-mipi-cal.h>
> > #include <linux/v4l2-mediabus.h>
> >
> > #include "vip.h"
> > @@ -42,6 +50,9 @@ enum tegra_vi_out {
> > #define VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT BIT(8)
> > #define VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT 0
> >
> > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(n) (0x0070 + (n) * 8)
> > +#define TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_END(n) (0x0074 + (n) * 8)
> > +
> > #define TEGRA_VI_VI_INPUT_CONTROL 0x0088
> > #define VI_INPUT_FIELD_DETECT BIT(27)
> > #define VI_INPUT_BT656 BIT(25)
> > @@ -88,6 +99,8 @@ enum tegra_vi_out {
> > #define VI_OUTPUT_OUTPUT_FORMAT_YUV422POST (3 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > #define VI_OUTPUT_OUTPUT_FORMAT_YUV420PLANAR (6 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > /* TEGRA_VI_OUT_2 supported formats */
> > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER (7 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > +#define VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER (8 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> > #define VI_OUTPUT_OUTPUT_FORMAT_VIP_BAYER_DIRECT (9 << VI_OUTPUT_OUTPUT_FORMAT_SFT)
> >
> > #define TEGRA_VI_VIP_H_ACTIVE 0x00a4
> > @@ -152,8 +165,106 @@ enum tegra_vi_out {
> > #define TEGRA_VI_VI_RAISE 0x01ac
> > #define VI_VI_RAISE_ON_EDGE BIT(0)
> >
> > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_START(n) (0x01d8 + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_RAISE_FRAME_END(n) (0x01dc + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_H_ACTIVE(n) (0x01e8 + (n) * 8)
> > +#define TEGRA_VI_CSI_PP_V_ACTIVE(n) (0x01ec + (n) * 8)
> > +
> > +/* Tegra20 CSI registers: Starts from 0x800, offset 0x0 */
> > +#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0000
> > +#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0008
> > +#define TEGRA_CSI_INPUT_STREAM_CONTROL(n) (0x0010 + (n) * 0x2c)
> > +#define CSI_SKIP_PACKET_THRESHOLD(n) (((n) & 0xff) << 16)
> > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL0(n) (0x0018 + (n) * 0x2c)
> > +#define CSI_PP_PAD_FRAME_PAD0S (0 << 28)
> > +#define CSI_PP_PAD_FRAME_PAD1S (1 << 28)
> > +#define CSI_PP_PAD_FRAME_NOPAD (2 << 28)
> > +#define CSI_PP_HEADER_EC_ENABLE BIT(27)
> > +#define CSI_PP_PAD_SHORT_LINE_PAD0S (0 << 24)
> > +#define CSI_PP_PAD_SHORT_LINE_PAD1S (1 << 24)
> > +#define CSI_PP_PAD_SHORT_LINE_NOPAD (2 << 24)
> > +#define CSI_PP_EMBEDDED_DATA_EMBEDDED BIT(20)
> > +#define CSI_PP_OUTPUT_FORMAT_ARBITRARY (0 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_PIXEL (1 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_PIXEL_REP (2 << 16)
> > +#define CSI_PP_OUTPUT_FORMAT_STORE (3 << 16)
> > +#define CSI_PP_VIRTUAL_CHANNEL_ID(n) (((n) - 1) << 14)
> > +#define CSI_PP_DATA_TYPE(n) ((n) << 8)
> > +#define CSI_PP_CRC_CHECK_ENABLE BIT(7)
> > +#define CSI_PP_WORD_COUNT_HEADER BIT(6)
> > +#define CSI_PP_DATA_IDENTIFIER_ENABLE BIT(5)
> > +#define CSI_PP_PACKET_HEADER_SENT BIT(4)
> > +#define TEGRA_CSI_PIXEL_STREAM_CONTROL1(n) (0x001c + (n) * 0x2c)
> > +#define TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(n) (0x0020 + (n) * 0x2c)
> > +#define TEGRA_CSI_PIXEL_STREAM_GAP(n) (0x0024 + (n) * 0x2c)
> > +#define CSI_PP_FRAME_MIN_GAP(n) (((n) & 0xffff) << 16)
> > +#define CSI_PP_LINE_MIN_GAP(n) (((n) & 0xffff))
> > +#define TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(n) (0x0028 + (n) * 0x2c)
> > +#define CSI_PP_START_MARKER_FRAME_MAX(n) (((n) & 0xf) << 12)
> > +#define CSI_PP_START_MARKER_FRAME_MIN(n) (((n) & 0xf) << 8)
> > +#define CSI_PP_VSYNC_START_MARKER BIT(4)
> > +#define CSI_PP_SINGLE_SHOT BIT(2)
> > +#define CSI_PP_NOP 0
> > +#define CSI_PP_ENABLE 1
> > +#define CSI_PP_DISABLE 2
> > +#define CSI_PP_RESET 3
> > +#define TEGRA_CSI_PHY_CIL_COMMAND 0x0068
> > +#define CSI_A_PHY_CIL_NOP 0x0
> > +#define CSI_A_PHY_CIL_ENABLE 0x1
> > +#define CSI_A_PHY_CIL_DISABLE 0x2
> > +#define CSI_A_PHY_CIL_ENABLE_MASK 0x3
> > +#define CSI_B_PHY_CIL_NOP (0x0 << 16)
> > +#define CSI_B_PHY_CIL_ENABLE (0x1 << 16)
> > +#define CSI_B_PHY_CIL_DISABLE (0x2 << 16)
> > +#define CSI_B_PHY_CIL_ENABLE_MASK (0x3 << 16)
> > +#define TEGRA_CSI_PHY_CIL_CONTROL0(n) (0x006c + (n) * 4)
> > +#define CSI_CONTINUOUS_CLOCK_MODE_ENABLE BIT(5)
> > +#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0078
> > +#define TEGRA_CSI_CSI_CIL_STATUS 0x007c
> > +#define CSI_MIPI_AUTO_CAL_DONE BIT(15)
> > +#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0080
> > +#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0084
> > +#define TEGRA_CSI_CSI_READONLY_STATUS 0x0088
> > +#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x008c
> > +#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0090
> > +#define TEGRA_CSI_CIL_PAD_CONFIG0(n) (0x0094 + (n) * 8)
> > +#define TEGRA_CSI_CIL_PAD_CONFIG1(n) (0x0098 + (n) * 8)
> > +#define TEGRA_CSI_CIL_PAD_CONFIG 0x00a4
> > +#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x00a8
> > +#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x00ac
> > +#define CSI_CIL_MIPI_CAL_STARTCAL BIT(31)
> > +#define CSI_CIL_MIPI_CAL_OVERIDE_A BIT(30)
> > +#define CSI_CIL_MIPI_CAL_OVERIDE_B BIT(30)
> > +#define CSI_CIL_MIPI_CAL_NOISE_FLT(n) (((n) & 0xf) << 26)
> > +#define CSI_CIL_MIPI_CAL_PRESCALE(n) (((n) & 0x3) << 24)
> > +#define CSI_CIL_MIPI_CAL_SEL_A BIT(21)
> > +#define CSI_CIL_MIPI_CAL_SEL_B BIT(21)
> > +#define CSI_CIL_MIPI_CAL_HSPDOS(n) (((n) & 0x1f) << 16)
> > +#define CSI_CIL_MIPI_CAL_HSPUOS(n) (((n) & 0x1f) << 8)
> > +#define CSI_CIL_MIPI_CAL_TERMOS(n) (((n) & 0x1f))
> > +#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x00b0
> > +#define TEGRA_CSI_CLKEN_OVERRIDE 0x00b4
> > +#define TEGRA_CSI_DEBUG_CONTROL 0x00b8
> > +#define CSI_DEBUG_CONTROL_DEBUG_EN_ENABLED BIT(0)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 BIT(4)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 BIT(5)
> > +#define CSI_DEBUG_CONTROL_CLR_DBG_CNT_2 BIT(6)
> > +#define CSI_DEBUG_CONTROL_DBG_CNT_SEL(n, v) ((v) << (8 + 8 * (n)))
> > +#define TEGRA_CSI_DEBUG_COUNTER(n) (0x00bc + (n) * 4)
> > +#define TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(n) (0x00c8 + (n) * 4)
> > +#define CSI_PP_EXP_FRAME_HEIGHT(n) (((n) & 0x1fff) << 16)
> > +#define CSI_PP_MAX_CLOCKS(n) (((n) & 0xfff) << 4)
> > +#define CSI_PP_LINE_TIMEOUT_ENABLE BIT(0)
> > +#define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x00d0
> > +#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG 0x00d4
> > +#define CSI_PAD_DRIV_DN_REF(n) (((n) & 0x7) << 16)
> > +#define CSI_PAD_DRIV_UP_REF(n) (((n) & 0x7) << 8)
> > +#define CSI_PAD_TERM_REF(n) (((n) & 0x7) << 0)
> > +#define TEGRA_CSI_CSI_CILA_STATUS 0x00d8
> > +#define TEGRA_CSI_CSI_CILB_STATUS 0x00dc
> > +
> > /* --------------------------------------------------------------------------
> > - * VI
> > + * Read and Write helpers
> > */
> >
> > static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u32 val)
> > @@ -161,6 +272,35 @@ static void tegra20_vi_write(struct tegra_vi_channel *chan, unsigned int addr, u
> > writel(val, chan->vi->iomem + addr);
> > }
> >
> > +static int __maybe_unused tegra20_vi_read(struct tegra_vi_channel *chan, unsigned int addr)
> > +{
> > + return readl(chan->vi->iomem + addr);
> > +}
> > +
> > +static void tegra20_csi_write(struct tegra_csi_channel *csi_chan, unsigned int addr, u32 val)
> > +{
> > + writel(val, csi_chan->csi->iomem + addr);
> > +}
> > +
> > +static int __maybe_unused tegra20_csi_read(struct tegra_csi_channel *csi_chan, unsigned int addr)
> > +{
> > + return readl(csi_chan->csi->iomem + addr);
> > +}
> > +
> > +static void tegra20_mipi_write(struct tegra_csi *csi, unsigned int addr, u32 val)
> > +{
> > + writel(val, csi->iomem + addr);
> > +}
> > +
> > +static int __maybe_unused tegra20_mipi_read(struct tegra_csi *csi, unsigned int addr)
> > +{
> > + return readl(csi->iomem + addr);
> > +}
> > +
> > +/* --------------------------------------------------------------------------
> > + * VI
> > + */
> > +
> > /*
> > * Get the main input format (YUV/RGB...) and the YUV variant as values to
> > * be written into registers for the current VI input mbus code.
> > @@ -283,20 +423,27 @@ static int tegra20_vi_enable(struct tegra_vi *vi, bool on)
> > static int tegra20_channel_host1x_syncpt_init(struct tegra_vi_channel *chan)
> > {
> > struct tegra_vi *vi = chan->vi;
> > - struct host1x_syncpt *out_sp;
> > + struct host1x_syncpt *out_sp, *fs_sp;
> >
> > out_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > if (!out_sp)
> > - return dev_err_probe(vi->dev, -ENOMEM, "failed to request syncpoint\n");
> > + return dev_err_probe(vi->dev, -EBUSY, "failed to request mw ack syncpoint\n");
> >
> > chan->mw_ack_sp[0] = out_sp;
> >
> > + fs_sp = host1x_syncpt_request(&vi->client, HOST1X_SYNCPT_CLIENT_MANAGED);
> > + if (!fs_sp)
> > + return dev_err_probe(vi->dev, -EBUSY, "failed to request frame start syncpoint\n");
> > +
> > + chan->frame_start_sp[0] = fs_sp;
> > +
> > return 0;
> > }
> >
> > static void tegra20_channel_host1x_syncpt_free(struct tegra_vi_channel *chan)
> > {
> > host1x_syncpt_put(chan->mw_ack_sp[0]);
> > + host1x_syncpt_put(chan->frame_start_sp[0]);
> > }
> >
> > static void tegra20_fmt_align(struct v4l2_pix_format *pix, unsigned int bpp)
> > @@ -418,41 +565,79 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan,
> > }
> >
> > static int tegra20_channel_capture_frame(struct tegra_vi_channel *chan,
> > - struct tegra_channel_buffer *buf)
> > + struct tegra_channel_buffer *buf,
> > + struct tegra_csi_channel *csi_chan)
> > {
> > + u32 val;
> > int err;
> >
> > - chan->next_out_sp_idx++;
> > -
> > tegra20_channel_vi_buffer_setup(chan, buf);
> >
> > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > + if (csi_chan) {
> > + u32 port = csi_chan->csi_port_nums[0] & 1;
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_SINGLE_SHOT | CSI_PP_ENABLE);
> > +
> > + val = host1x_syncpt_read(chan->frame_start_sp[0]);
> > + do {
> > + err = host1x_syncpt_wait(chan->frame_start_sp[0],
> > + val + 1, TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > + } while (err == -ERESTARTSYS);
>
> This function is called only from a kthread, so I don't think it's possible for any functions to return -ERESTARTSYS. Have you seen otherwise? (Anyway, it it were possible, we should add a parameter to host1x_syncpt_wait to specify whether the wait should be interruptible or not, instead of working around it)
>
I am informing you regarding -ERESTARTSYS, it does not appear on
single shots or low resolution (2MP) shot sequences, but pops if
resolution (8MP) or framerate are high. I will keep this -ERESTARTSYS
workaround for now and it can be revised in followups later.
> > +
> > + if (err) {
> > + if (err != -ERESTARTSYS)
> > + dev_err_ratelimited(&chan->video.dev,
> > + "frame start syncpt timeout: %d\n", err);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) | CSI_PP_RESET);
> > + goto exit;
> > + }
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > + } else {
> > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_VIP_ENABLE);
> > + }
> > +
> > + val = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > + do {
> > + err = host1x_syncpt_wait(chan->mw_ack_sp[0], val + 1,
> > + TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > + } while (err == -ERESTARTSYS);
> >
> > - /* Wait for syncpt counter to reach frame start event threshold */
> > - err = host1x_syncpt_wait(chan->mw_ack_sp[0], chan->next_out_sp_idx,
> > - TEGRA_VI_SYNCPT_WAIT_TIMEOUT, NULL);
> > if (err) {
> > - host1x_syncpt_incr(chan->mw_ack_sp[0]);
> > - dev_err_ratelimited(&chan->video.dev, "frame start syncpt timeout: %d\n", err);
> > - release_buffer(chan, buf, VB2_BUF_STATE_ERROR);
> > - return err;
> > + if (err != -ERESTARTSYS)
> > + dev_err_ratelimited(&chan->video.dev, "mw ack syncpt timeout: %d\n", err);
> > + goto exit;
> > }
> >
> > - tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > - VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> > + if (!csi_chan)
> > + tegra20_vi_write(chan, TEGRA_VI_CAMERA_CONTROL,
> > + VI_CAMERA_CONTROL_STOP_CAPTURE | VI_CAMERA_CONTROL_VIP_ENABLE);
> >
> > +exit:
> > release_buffer(chan, buf, VB2_BUF_STATE_DONE);
> >
> > - return 0;
> > + return err;
> > }
> >
> > static int tegra20_chan_capture_kthread_start(void *data)
> > {
> > struct tegra_vi_channel *chan = data;
> > struct tegra_channel_buffer *buf;
> > + struct v4l2_subdev *csi_subdev = NULL;
> > + struct tegra_csi_channel *csi_chan = NULL;
> > unsigned int retries = 0;
> > int err = 0;
> >
> > + csi_subdev = tegra_channel_get_remote_csi_subdev(chan);
> > + if (csi_subdev)
> > + csi_chan = to_csi_chan(csi_subdev);
> > +
> > while (1) {
> > /*
> > * Source is not streaming if error is non-zero.
> > @@ -477,7 +662,7 @@ static int tegra20_chan_capture_kthread_start(void *data)
> > list_del_init(&buf->queue);
> > spin_unlock(&chan->start_lock);
> >
> > - err = tegra20_channel_capture_frame(chan, buf);
> > + err = tegra20_channel_capture_frame(chan, buf, csi_chan);
> > if (!err) {
> > retries = 0;
> > continue;
> > @@ -504,28 +689,6 @@ static void tegra20_camera_capture_setup(struct tegra_vi_channel *chan)
> > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > - int main_output_format;
> > - int yuv_output_format;
> > -
> > - tegra20_vi_get_output_formats(chan, &main_output_format, &yuv_output_format);
> > -
> > - /*
> > - * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > - * for luminance, which is the default and means not to touch
> > - * anything.
> > - */
> > - tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > - 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > - 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > -
> > - /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > - tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> > -
> > - tegra20_vi_write(chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > - (chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > - (chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > - yuv_output_format << VI_OUTPUT_YUV_OUTPUT_FORMAT_SFT |
> > - main_output_format << VI_OUTPUT_OUTPUT_FORMAT_SFT);
> >
> > /* Set up frame size */
> > tegra20_vi_write(chan, TEGRA_VI_OUTPUT_FRAME_SIZE(output_channel),
> > @@ -556,18 +719,28 @@ static int tegra20_vi_start_streaming(struct vb2_queue *vq, u32 count)
> > struct media_pipeline *pipe = &chan->video.pipe;
> > int err;
> >
> > - chan->next_out_sp_idx = host1x_syncpt_read(chan->mw_ack_sp[0]);
> > -
> > err = video_device_pipeline_start(&chan->video, pipe);
> > if (err)
> > goto error_pipeline_start;
> >
> > - tegra20_camera_capture_setup(chan);
> > + /*
> > + * Set up low pass filter. Use 0x240 for chromaticity and 0x240
> > + * for luminance, which is the default and means not to touch
> > + * anything.
> > + */
> > + tegra20_vi_write(chan, TEGRA_VI_H_LPF_CONTROL,
> > + 0x0240 << VI_H_LPF_CONTROL_LUMA_SFT |
> > + 0x0240 << VI_H_LPF_CONTROL_CHROMA_SFT);
> > +
> > + /* Set up raise-on-edge, so we get an interrupt on end of frame. */
> > + tegra20_vi_write(chan, TEGRA_VI_VI_RAISE, VI_VI_RAISE_ON_EDGE);
> >
> > err = tegra_channel_set_stream(chan, true);
> > if (err)
> > goto error_set_stream;
> >
> > + tegra20_camera_capture_setup(chan);
> > +
> > chan->sequence = 0;
> >
> > chan->kthread_start_capture = kthread_run(tegra20_chan_capture_kthread_start,
> > @@ -662,6 +835,345 @@ const struct tegra_vi_soc tegra20_vi_soc = {
> > .has_h_v_flip = true,
> > };
> >
> > +/* --------------------------------------------------------------------------
> > + * MIPI Calibration
> > + */
> > +static int tegra20_start_pad_calibration(struct tegra_mipi_device *mipi)
> > +{
> > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > + unsigned int port = mipi->pads;
> > + u32 value;
> > + int ret;
> > +
> > + guard(mutex)(&csi->mipi_lock);
> > +
> > + ret = pm_runtime_resume_and_get(csi->dev);
> > + if (ret < 0) {
> > + dev_err(csi->dev, "failed to get runtime PM: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_DSI_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_HSPDOS(4) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(3) |
> > + CSI_CIL_MIPI_CAL_TERMOS(0));
> > + tegra20_mipi_write(csi, TEGRA_CSI_MIPIBIAS_PAD_CONFIG,
> > + CSI_PAD_DRIV_DN_REF(5) |
> > + CSI_PAD_DRIV_UP_REF(7) |
> > + CSI_PAD_TERM_REF(0));
> > +
> > + /* CSI B */
> > + value = CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > +
> > + if (port == PORT_B)
> > + value |= CSI_CIL_MIPI_CAL_SEL_B;
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, value);
> > +
> > + /* CSI A */
> > + value = CSI_CIL_MIPI_CAL_STARTCAL |
> > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4);
> > +
> > + if (port == PORT_A)
> > + value |= CSI_CIL_MIPI_CAL_SEL_A;
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, value);
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int tegra20_finish_pad_calibration(struct tegra_mipi_device *mipi)
> > +{
> > + struct tegra_csi *csi = platform_get_drvdata(mipi->pdev);
> > + void __iomem *cil_status_reg = csi->iomem + TEGRA_CSI_CSI_CIL_STATUS;
> > + unsigned int port = mipi->pads;
> > + u32 value, pp = 0, cil = 0;
> > + int ret;
> > +
> > + /* This part is only for CSI */
> > + if (port > PORT_B) {
> > + pm_runtime_put(csi->dev);
> > +
> > + return 0;
> > + }
> > +
> > + guard(mutex)(&csi->mipi_lock);
> > +
> > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > + value & CSI_MIPI_AUTO_CAL_DONE, 50, 250000);
> > + if (ret < 0) {
> > + dev_warn(csi->dev, "MIPI calibration timeout!\n");
> > + goto exit;
> > + }
> > +
> > + /* clear status */
> > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, value);
> > + ret = readl_relaxed_poll_timeout(cil_status_reg, value,
> > + !(value & CSI_MIPI_AUTO_CAL_DONE), 50, 250000);
> > + if (ret < 0) {
> > + dev_warn(csi->dev, "MIPI calibration status timeout!\n");
> > + goto exit;
> > + }
> > +
> > + pp = tegra20_mipi_read(csi, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > + cil = tegra20_mipi_read(csi, TEGRA_CSI_CSI_CIL_STATUS);
> > + if (pp | cil) {
> > + dev_warn(csi->dev, "Calibration status not been cleared!\n");
> > + ret = -EINVAL;
> > + goto exit;
> > + }
> > +
> > +exit:
> > + tegra20_mipi_write(csi, TEGRA_CSI_CSI_CIL_STATUS, pp);
> > +
> > + /* un-select to avoid interference with DSI */
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILB_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > +
> > + tegra20_mipi_write(csi, TEGRA_CSI_CILA_MIPI_CAL_CONFIG,
> > + CSI_CIL_MIPI_CAL_NOISE_FLT(0xa) |
> > + CSI_CIL_MIPI_CAL_PRESCALE(0x2) |
> > + CSI_CIL_MIPI_CAL_HSPDOS(0) |
> > + CSI_CIL_MIPI_CAL_HSPUOS(0) |
> > + CSI_CIL_MIPI_CAL_TERMOS(4));
> > +
> > + pm_runtime_put(csi->dev);
> > +
> > + return ret;
> > +}
> > +
> > +static const struct tegra_mipi_ops tegra20_mipi_ops = {
> > + .start_calibration = tegra20_start_pad_calibration,
> > + .finish_calibration = tegra20_finish_pad_calibration,
> > +};
> > +
> > +/* --------------------------------------------------------------------------
> > + * CSI
> > + */
> > +static void tegra20_csi_capture_clean(struct tegra_csi_channel *csi_chan)
> > +{
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_READONLY_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_ESCAPE_MODE_DATA, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CLKEN_OVERRIDE, 0);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_DEBUG_CONTROL,
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_0 |
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_1 |
> > + CSI_DEBUG_CONTROL_CLR_DBG_CNT_2);
> > +}
> > +
> > +static int tegra20_csi_port_start_streaming(struct tegra_csi_channel *csi_chan,
> > + u8 portno)
> > +{
> > + struct tegra_vi_channel *vi_chan = v4l2_get_subdev_hostdata(&csi_chan->subdev);
> > + int width = vi_chan->format.width;
> > + int height = vi_chan->format.height;
> > + u32 data_type = vi_chan->fmtinfo->img_dt;
> > + u32 word_count = (width * vi_chan->fmtinfo->bit_width) / 8;
> > + enum tegra_vi_out output_channel = TEGRA_VI_OUT_1;
> > +
> > + unsigned int main_output_format, yuv_output_format;
> > + unsigned int port = portno & 1;
> > + u32 value;
> > +
> > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> > +
> > + switch (data_type) {
> > + case TEGRA_IMAGE_DT_RAW8:
> > + case TEGRA_IMAGE_DT_RAW10:
> > + output_channel = TEGRA_VI_OUT_2;
> > + if (port == PORT_A)
> > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPA_BAYER;
> > + else
> > + main_output_format = VI_OUTPUT_OUTPUT_FORMAT_CSI_PPB_BAYER;
> > + break;
> > + }
> > +
> > + tegra20_csi_capture_clean(csi_chan);
> > +
> > + /* CSI port cleanup */
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG0(port), 0);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CIL_PAD_CONFIG1(port), 0);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, BIT(25 + port)); /* CSI_PP_YUV422 */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_H_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > + tegra20_vi_write(vi_chan, TEGRA_VI_V_DOWNSCALE_CONTROL, BIT(2 + port)); /* CSI_PP */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_H_ACTIVE(port), width << 16);
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CSI_PP_V_ACTIVE(port), height << 16);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL1(port), 0x1);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_WORD_COUNT(port), word_count);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_GAP(port),
> > + CSI_PP_FRAME_MIN_GAP(0x14)); /* 14 vi clks between frames */
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME(port),
> > + CSI_PP_EXP_FRAME_HEIGHT(height) |
> > + CSI_PP_MAX_CLOCKS(0x300) | /* wait 0x300 vi clks for timeout */
> > + CSI_PP_LINE_TIMEOUT_ENABLE);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_CONTROL0(port),
> > + CSI_PP_OUTPUT_FORMAT_PIXEL |
> > + CSI_PP_DATA_TYPE(data_type) |
> > + CSI_PP_CRC_CHECK_ENABLE |
> > + CSI_PP_WORD_COUNT_HEADER |
> > + CSI_PP_DATA_IDENTIFIER_ENABLE |
> > + CSI_PP_PACKET_HEADER_SENT |
> > + port);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_INPUT_STREAM_CONTROL(port),
> > + CSI_SKIP_PACKET_THRESHOLD(0x3f) |
> > + (csi_chan->numlanes - 1));
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_CONTROL0(port),
> > + CSI_CONTINUOUS_CLOCK_MODE_ENABLE |
> > + 0x5); /* Clock settle time */
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_CSI_PP_FRAME_START(port),
> > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > + host1x_syncpt_id(vi_chan->frame_start_sp[0])
> > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_CONT_SYNCPT_OUT(output_channel),
> > + VI_CONT_SYNCPT_OUT_CONTINUOUS_SYNCPT |
> > + host1x_syncpt_id(vi_chan->mw_ack_sp[0])
> > + << VI_CONT_SYNCPT_OUT_SYNCPT_IDX_SFT);
> > +
> > + value = (port == PORT_A) ? CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_DISABLE :
> > + CSI_B_PHY_CIL_ENABLE | CSI_A_PHY_CIL_DISABLE;
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > +
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > + yuv_output_format | main_output_format);
> > +
> > + return 0;
> > +};
> > +
> > +static void tegra20_csi_port_stop_streaming(struct tegra_csi_channel *csi_chan, u8 portno)
> > +{
> > + struct tegra_csi *csi = csi_chan->csi;
> > + unsigned int port = portno & 1;
> > + u32 value;
> > +
> > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS);
> > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x%08x\n", value);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, value);
> > +
> > + value = tegra20_csi_read(csi_chan, TEGRA_CSI_CSI_CIL_STATUS);
> > + dev_dbg(csi->dev, "TEGRA_CSI_CSI_CIL_STATUS 0x%08x\n", value);
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_CSI_CIL_STATUS, value);
> > +
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND(port),
> > + CSI_PP_START_MARKER_FRAME_MAX(0xf) |
> > + CSI_PP_DISABLE);
> > +
> > + if (csi_chan->numlanes == 4) {
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND,
> > + CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_DISABLE);
> > + } else {
> > + value = (port == PORT_A) ? CSI_A_PHY_CIL_DISABLE | CSI_B_PHY_CIL_NOP :
> > + CSI_B_PHY_CIL_DISABLE | CSI_A_PHY_CIL_NOP;
> > + tegra20_csi_write(csi_chan, TEGRA_CSI_PHY_CIL_COMMAND, value);
> > + }
> > +}
> > +
> > +static int tegra20_csi_start_streaming(struct tegra_csi_channel *csi_chan)
> > +{
> > + u8 *portnos = csi_chan->csi_port_nums;
> > + int ret, i;
> > +
> > + for (i = 0; i < csi_chan->numgangports; i++) {
> > + ret = tegra20_csi_port_start_streaming(csi_chan, portnos[i]);
> > + if (ret)
> > + goto stream_start_fail;
> > + }
> > +
> > + return 0;
> > +
> > +stream_start_fail:
> > + for (i = i - 1; i >= 0; i--)
> > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > +
> > + return ret;
> > +}
> > +
> > +static void tegra20_csi_stop_streaming(struct tegra_csi_channel *csi_chan)
> > +{
> > + u8 *portnos = csi_chan->csi_port_nums;
> > + int i;
> > +
> > + for (i = 0; i < csi_chan->numgangports; i++)
> > + tegra20_csi_port_stop_streaming(csi_chan, portnos[i]);
> > +}
> > +
> > +static const struct tegra_csi_ops tegra20_csi_ops = {
> > + .csi_start_streaming = tegra20_csi_start_streaming,
> > + .csi_stop_streaming = tegra20_csi_stop_streaming,
> > +};
> > +
> > +static const char * const tegra20_csi_clks[] = {
> > + NULL,
> > +};
> > +
> > +const struct tegra_csi_soc tegra20_csi_soc = {
> > + .ops = &tegra20_csi_ops,
> > + .mipi_ops = &tegra20_mipi_ops,
> > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > + .clk_names = tegra20_csi_clks,
> > + .num_clks = ARRAY_SIZE(tegra20_csi_clks),
> > +};
> > +
> > +static const char * const tegra30_csi_clks[] = {
> > + "csi",
> > + "csia-pad",
> > + "csib-pad",
> > +};
> > +
> > +const struct tegra_csi_soc tegra30_csi_soc = {
> > + .ops = &tegra20_csi_ops,
> > + .mipi_ops = &tegra20_mipi_ops,
> > + .csi_max_channels = 2, /* CSI-A and CSI-B */
> > + .clk_names = tegra30_csi_clks,
> > + .num_clks = ARRAY_SIZE(tegra30_csi_clks),
> > +};
> > +
> > /* --------------------------------------------------------------------------
> > * VIP
> > */
> > @@ -681,10 +1193,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> > enum tegra_vi_out output_channel = (data_type == TEGRA_IMAGE_DT_RAW8 ||
> > data_type == TEGRA_IMAGE_DT_RAW10) ?
> > TEGRA_VI_OUT_2 : TEGRA_VI_OUT_1;
> > - unsigned int main_input_format;
> > - unsigned int yuv_input_format;
> > + unsigned int main_input_format, yuv_input_format;
> > + unsigned int main_output_format, yuv_output_format;
> >
> > tegra20_vi_get_input_formats(vi_chan, &main_input_format, &yuv_input_format);
> > + tegra20_vi_get_output_formats(vi_chan, &main_output_format, &yuv_output_format);
> >
> > tegra20_vi_write(vi_chan, TEGRA_VI_VI_CORE_CONTROL, 0);
> >
> > @@ -717,6 +1230,11 @@ static int tegra20_vip_start_streaming(struct tegra_vip_channel *vip_chan)
> >
> > tegra20_vi_write(vi_chan, TEGRA_VI_CAMERA_CONTROL, VI_CAMERA_CONTROL_STOP_CAPTURE);
> >
> > + tegra20_vi_write(vi_chan, TEGRA_VI_VI_OUTPUT_CONTROL(output_channel),
> > + (vi_chan->vflip ? VI_OUTPUT_V_DIRECTION : 0) |
> > + (vi_chan->hflip ? VI_OUTPUT_H_DIRECTION : 0) |
> > + yuv_output_format | main_output_format);
> > +
> > return 0;
> > }
> >
> > diff --git a/drivers/staging/media/tegra-video/vi.h b/drivers/staging/media/tegra-video/vi.h
> > index 5cbc0606ed6c..bad55e0bd313 100644
> > --- a/drivers/staging/media/tegra-video/vi.h
> > +++ b/drivers/staging/media/tegra-video/vi.h
> > @@ -125,7 +125,6 @@ struct tegra_vi {
> > * frame through host1x syncpoint counters (On Tegra20 used for the
> > * OUT_1 syncpt)
> > * @sp_incr_lock: protects cpu syncpoint increment.
> > - * @next_out_sp_idx: next expected value for mw_ack_sp[0], i.e. OUT_1 (Tegra20)
> > *
> > * @kthread_start_capture: kthread to start capture of single frame when
> > * vb buffer is available. This thread programs VI CSI hardware
> > @@ -188,7 +187,6 @@ struct tegra_vi_channel {
> > struct host1x_syncpt *mw_ack_sp[GANG_PORTS_MAX];
> > /* protects the cpu syncpoint increment */
> > spinlock_t sp_incr_lock[GANG_PORTS_MAX];
> > - u32 next_out_sp_idx;
> >
> > struct task_struct *kthread_start_capture;
> > wait_queue_head_t start_wait;
> > diff --git a/drivers/staging/media/tegra-video/video.c b/drivers/staging/media/tegra-video/video.c
> > index 6fe8d5301b9c..9f2bddc460bf 100644
> > --- a/drivers/staging/media/tegra-video/video.c
> > +++ b/drivers/staging/media/tegra-video/video.c
> > @@ -127,6 +127,12 @@ static const struct of_device_id host1x_video_subdevs[] = {
> > { .compatible = "nvidia,tegra20-vip", },
> > { .compatible = "nvidia,tegra20-vi", },
> > #endif
> > +#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
> > + { .compatible = "nvidia,tegra20-csi", },
> > +#endif
> > +#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
> > + { .compatible = "nvidia,tegra30-csi", },
> > +#endif
> > #if defined(CONFIG_ARCH_TEGRA_210_SOC)
> > { .compatible = "nvidia,tegra210-csi", },
> > { .compatible = "nvidia,tegra210-vi", },
> >
>
>
>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-02 6:20 ` Svyatoslav Ryhel
@ 2025-10-06 18:54 ` Luca Ceresoli
2025-10-07 16:02 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Luca Ceresoli @ 2025-10-06 18:54 UTC (permalink / raw)
To: Svyatoslav Ryhel, Mikko Perttunen
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Prashant Gaikwad, Michael Turquette, Stephen Boyd, Linus Walleij,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann, dri-devel, devicetree, linux-tegra, linux-kernel,
linux-media, linux-clk, linux-gpio, linux-staging
Hello Svyatoslav,
On Thu Oct 2, 2025 at 8:20 AM CEST, Svyatoslav Ryhel wrote:
>> > > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
>> > > > plane and 2 for V plane, total is 12. "but explainable with a comment
>> > > > and improve-able later" why then we cannot use 12 with a comment? this
>> > > > is all arbitrary. Downstream is not wrong from this perspective, you
>> > > > don't take into account that YUV420 is planar and it uses 3 planes a
>> > > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
>> > > > 1/4 width which is width * 3/2
>> > >
>> > > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
>> > >
>> > https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
>>
>> I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
>
> No, it has direct impact. This is how buffer size / image size is
> calculated since we place each plane consecutive. And bytes per line
> is used specifically in image size calculation. This is common part
> with non-planar formats. Then since Tegra provides a dedicated
> channels/buffers for each plane, configuration of planar format
> includes an additional step with calculation for each plane.
Sorry, I haven't followed the discussion in detail, but I tested you series
on Tegra20 VIP and capture does not work, with a SIGSEGV in
gstreamer. Bisecting pointed to this as the first commit where the issue
happens.
I compared the input and output values of tegra20_fmt_align() at this
commit and at the previous one, and this is the result:
before this patch with this patch
At function entry:
bpp 1 12
pix->width 640 640
pix->height 480 480
On return:
pix->bytesperline 640 960
pix->sizeimage 460800 460800
I hope these info will help.
Best regards,
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
2025-10-02 5:14 ` Svyatoslav Ryhel
@ 2025-10-06 20:31 ` Rob Herring
2025-10-07 5:13 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Rob Herring @ 2025-10-06 20:31 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann, dri-devel,
devicetree, linux-tegra, linux-kernel, linux-media, linux-clk,
linux-gpio, linux-staging
On Thu, Oct 02, 2025 at 08:14:22AM +0300, Svyatoslav Ryhel wrote:
> чт, 2 жовт. 2025 р. о 04:52 Rob Herring <robh@kernel.org> пише:
> >
> > On Thu, Sep 25, 2025 at 06:16:46PM +0300, Svyatoslav Ryhel wrote:
> > > Document CSI HW block found in Tegra20 and Tegra30 SoC.
> > >
> > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > ---
> > > .../display/tegra/nvidia,tegra20-csi.yaml | 135 ++++++++++++++++++
> > > 1 file changed, 135 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > > new file mode 100644
> > > index 000000000000..817b3097846b
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > > @@ -0,0 +1,135 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-csi.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: NVIDIA Tegra20 CSI controller
> > > +
> > > +maintainers:
> > > + - Svyatoslav Ryhel <clamor95@gmail.com>
> > > +
> > > +properties:
> > > + compatible:
> > > + enum:
> > > + - nvidia,tegra20-csi
> > > + - nvidia,tegra30-csi
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + clocks: true
> > > + clock-names: true
> > > +
> > > + avdd-dsi-csi-supply:
> > > + description: DSI/CSI power supply. Must supply 1.2 V.
> > > +
> > > + power-domains:
> > > + maxItems: 1
> > > +
> > > + "#nvidia,mipi-calibrate-cells":
> > > + description:
> > > + The number of cells in a MIPI calibration specifier. Should be 1.
> > > + The single cell specifies an id of the pad that need to be
> > > + calibrated for a given device. Valid pad ids for receiver would be
> > > + 0 for CSI-A; 1 for CSI-B; 2 for DSI-A and 3 for DSI-B.
> > > + $ref: /schemas/types.yaml#/definitions/uint32
> > > + const: 1
> >
> > Sorry I didn't bring this up before, but is this ever not 1? If it is
> > fixed, then you don't really need the property. I prefer it just be
> > fixed rather than getting a bunch of vendor specific #foo-cells.
> >
>
> This is not an introduction of property, such property already exists
> in Documentation/devicetree/bindings/display/tegra/nvidia,tegra114-mipi.yaml
> and is used in multiple device trees. As I have told before, in case
> of Tegra30 and Tegra20 CSI block combines mipi calibration function
> and CSI function, in Tegra114+ mipi calibration got a dedicated
> hardware block which is already supported. This property here is used
> to align with mipi-calibration logic used by Tegra114+
Okay.
You will have to continue to tell me again if my past questions are not
addressed in the commit message. A review only last week was 100+
patches ago. Don't expect I'll remember nor go re-read prior versions.
Ideally, we don't define the type of a property more than once. So this
should really first be moved to its own shared schema that's referenced
here and in the original user. Then it is perfectly clear reading the
patches that this is not a new property.
Rob
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI
2025-10-06 20:31 ` Rob Herring
@ 2025-10-07 5:13 ` Svyatoslav Ryhel
0 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-07 5:13 UTC (permalink / raw)
To: Rob Herring
Cc: David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, Krzysztof Kozlowski, Conor Dooley,
Thierry Reding, Jonathan Hunter, Sowjanya Komatineni,
Luca Ceresoli, Prashant Gaikwad, Michael Turquette, Stephen Boyd,
Mikko Perttunen, Linus Walleij, Mauro Carvalho Chehab,
Greg Kroah-Hartman, Jonas Schwöbel, Dmitry Osipenko,
Charan Pedumuru, Diogo Ivo, Aaron Kling, Arnd Bergmann, dri-devel,
devicetree, linux-tegra, linux-kernel, linux-media, linux-clk,
linux-gpio, linux-staging
пн, 6 жовт. 2025 р. о 23:31 Rob Herring <robh@kernel.org> пише:
>
> On Thu, Oct 02, 2025 at 08:14:22AM +0300, Svyatoslav Ryhel wrote:
> > чт, 2 жовт. 2025 р. о 04:52 Rob Herring <robh@kernel.org> пише:
> > >
> > > On Thu, Sep 25, 2025 at 06:16:46PM +0300, Svyatoslav Ryhel wrote:
> > > > Document CSI HW block found in Tegra20 and Tegra30 SoC.
> > > >
> > > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> > > > ---
> > > > .../display/tegra/nvidia,tegra20-csi.yaml | 135 ++++++++++++++++++
> > > > 1 file changed, 135 insertions(+)
> > > > create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > > > new file mode 100644
> > > > index 000000000000..817b3097846b
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
> > > > @@ -0,0 +1,135 @@
> > > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-csi.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: NVIDIA Tegra20 CSI controller
> > > > +
> > > > +maintainers:
> > > > + - Svyatoslav Ryhel <clamor95@gmail.com>
> > > > +
> > > > +properties:
> > > > + compatible:
> > > > + enum:
> > > > + - nvidia,tegra20-csi
> > > > + - nvidia,tegra30-csi
> > > > +
> > > > + reg:
> > > > + maxItems: 1
> > > > +
> > > > + clocks: true
> > > > + clock-names: true
> > > > +
> > > > + avdd-dsi-csi-supply:
> > > > + description: DSI/CSI power supply. Must supply 1.2 V.
> > > > +
> > > > + power-domains:
> > > > + maxItems: 1
> > > > +
> > > > + "#nvidia,mipi-calibrate-cells":
> > > > + description:
> > > > + The number of cells in a MIPI calibration specifier. Should be 1.
> > > > + The single cell specifies an id of the pad that need to be
> > > > + calibrated for a given device. Valid pad ids for receiver would be
> > > > + 0 for CSI-A; 1 for CSI-B; 2 for DSI-A and 3 for DSI-B.
> > > > + $ref: /schemas/types.yaml#/definitions/uint32
> > > > + const: 1
> > >
> > > Sorry I didn't bring this up before, but is this ever not 1? If it is
> > > fixed, then you don't really need the property. I prefer it just be
> > > fixed rather than getting a bunch of vendor specific #foo-cells.
> > >
> >
> > This is not an introduction of property, such property already exists
> > in Documentation/devicetree/bindings/display/tegra/nvidia,tegra114-mipi.yaml
> > and is used in multiple device trees. As I have told before, in case
> > of Tegra30 and Tegra20 CSI block combines mipi calibration function
> > and CSI function, in Tegra114+ mipi calibration got a dedicated
> > hardware block which is already supported. This property here is used
> > to align with mipi-calibration logic used by Tegra114+
>
> Okay.
>
> You will have to continue to tell me again if my past questions are not
> addressed in the commit message. A review only last week was 100+
> patches ago. Don't expect I'll remember nor go re-read prior versions.
>
That is not a problem, I did not meant to offend you. I will add info
into commit message.
> Ideally, we don't define the type of a property more than once. So this
> should really first be moved to its own shared schema that's referenced
> here and in the original user. Then it is perfectly clear reading the
> patches that this is not a new property.
>
I am not sure that creating a dedicated shared schema for a single
properly which is used by 2 schemas worth it, though, if it is
preferred, may the refactoring be done in followups later?
> Rob
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-06 18:54 ` Luca Ceresoli
@ 2025-10-07 16:02 ` Svyatoslav Ryhel
2025-10-07 19:37 ` Luca Ceresoli
0 siblings, 1 reply; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-07 16:02 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Mikko Perttunen, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Sowjanya Komatineni, Prashant Gaikwad,
Michael Turquette, Stephen Boyd, Linus Walleij,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann, dri-devel, devicetree, linux-tegra, linux-kernel,
linux-media, linux-clk, linux-gpio, linux-staging
пн, 6 жовт. 2025 р. о 21:55 Luca Ceresoli <luca.ceresoli@bootlin.com> пише:
>
> Hello Svyatoslav,
>
> On Thu Oct 2, 2025 at 8:20 AM CEST, Svyatoslav Ryhel wrote:
> >> > > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> >> > > > plane and 2 for V plane, total is 12. "but explainable with a comment
> >> > > > and improve-able later" why then we cannot use 12 with a comment? this
> >> > > > is all arbitrary. Downstream is not wrong from this perspective, you
> >> > > > don't take into account that YUV420 is planar and it uses 3 planes a
> >> > > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> >> > > > 1/4 width which is width * 3/2
> >> > >
> >> > > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
> >> > >
> >> > https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
> >>
> >> I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
> >
> > No, it has direct impact. This is how buffer size / image size is
> > calculated since we place each plane consecutive. And bytes per line
> > is used specifically in image size calculation. This is common part
> > with non-planar formats. Then since Tegra provides a dedicated
> > channels/buffers for each plane, configuration of planar format
> > includes an additional step with calculation for each plane.
>
> Sorry, I haven't followed the discussion in detail, but I tested you series
> on Tegra20 VIP and capture does not work, with a SIGSEGV in
> gstreamer. Bisecting pointed to this as the first commit where the issue
> happens.
>
> I compared the input and output values of tegra20_fmt_align() at this
> commit and at the previous one, and this is the result:
>
> before this patch with this patch
> At function entry:
> bpp 1 12
> pix->width 640 640
> pix->height 480 480
>
> On return:
> pix->bytesperline 640 960
> pix->sizeimage 460800 460800
>
> I hope these info will help.
Which command did you use? I have tested with ffmpeg and
yuv422/yuv420p and it worked perfectly fine.
> Best regards,
> Luca
>
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-07 16:02 ` Svyatoslav Ryhel
@ 2025-10-07 19:37 ` Luca Ceresoli
2025-10-08 5:44 ` Svyatoslav Ryhel
0 siblings, 1 reply; 50+ messages in thread
From: Luca Ceresoli @ 2025-10-07 19:37 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Mikko Perttunen, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Sowjanya Komatineni, Prashant Gaikwad,
Michael Turquette, Stephen Boyd, Linus Walleij,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann, dri-devel, devicetree, linux-tegra, linux-kernel,
linux-media, linux-clk, linux-gpio, linux-staging
Hello Svyatoslav,
On Tue Oct 7, 2025 at 6:02 PM CEST, Svyatoslav Ryhel wrote:
> пн, 6 жовт. 2025 р. о 21:55 Luca Ceresoli <luca.ceresoli@bootlin.com> пише:
>>
>> Hello Svyatoslav,
>>
>> On Thu Oct 2, 2025 at 8:20 AM CEST, Svyatoslav Ryhel wrote:
>> >> > > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
>> >> > > > plane and 2 for V plane, total is 12. "but explainable with a comment
>> >> > > > and improve-able later" why then we cannot use 12 with a comment? this
>> >> > > > is all arbitrary. Downstream is not wrong from this perspective, you
>> >> > > > don't take into account that YUV420 is planar and it uses 3 planes a
>> >> > > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
>> >> > > > 1/4 width which is width * 3/2
>> >> > >
>> >> > > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
>> >> > >
>> >> > https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
>> >>
>> >> I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
>> >
>> > No, it has direct impact. This is how buffer size / image size is
>> > calculated since we place each plane consecutive. And bytes per line
>> > is used specifically in image size calculation. This is common part
>> > with non-planar formats. Then since Tegra provides a dedicated
>> > channels/buffers for each plane, configuration of planar format
>> > includes an additional step with calculation for each plane.
>>
>> Sorry, I haven't followed the discussion in detail, but I tested you series
>> on Tegra20 VIP and capture does not work, with a SIGSEGV in
>> gstreamer. Bisecting pointed to this as the first commit where the issue
>> happens.
>>
>> I compared the input and output values of tegra20_fmt_align() at this
>> commit and at the previous one, and this is the result:
>>
>> before this patch with this patch
>> At function entry:
>> bpp 1 12
>> pix->width 640 640
>> pix->height 480 480
>>
>> On return:
>> pix->bytesperline 640 960
>> pix->sizeimage 460800 460800
>>
>> I hope these info will help.
>
> Which command did you use? I have tested with ffmpeg and
> yuv422/yuv420p and it worked perfectly fine.
I have a simple testing script that runs these commands, with
VNODE="/dev/video0":
v4l2-ctl -d ${VNODE} --set-ctrl horizontal_flip=1 --set-ctrl vertical_flip=1
gst-launch-1.0 -ve v4l2src device=${VNODE} num-buffers=500 \
! video/x-raw,width=640,height=480,framerate=50/1,format=I420 \
! videorate drop-only=true skip-to-first=true \
! video/x-raw,framerate=50/4 \
! queue \
! avenc_mpeg4 \
! mp4mux \
! filesink location=/tmp/grab.mp4
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations
2025-10-07 19:37 ` Luca Ceresoli
@ 2025-10-08 5:44 ` Svyatoslav Ryhel
0 siblings, 0 replies; 50+ messages in thread
From: Svyatoslav Ryhel @ 2025-10-08 5:44 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Mikko Perttunen, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, Sowjanya Komatineni, Prashant Gaikwad,
Michael Turquette, Stephen Boyd, Linus Walleij,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Jonas Schwöbel,
Dmitry Osipenko, Charan Pedumuru, Diogo Ivo, Aaron Kling,
Arnd Bergmann, dri-devel, devicetree, linux-tegra, linux-kernel,
linux-media, linux-clk, linux-gpio, linux-staging
вт, 7 жовт. 2025 р. о 22:37 Luca Ceresoli <luca.ceresoli@bootlin.com> пише:
>
> Hello Svyatoslav,
>
> On Tue Oct 7, 2025 at 6:02 PM CEST, Svyatoslav Ryhel wrote:
> > пн, 6 жовт. 2025 р. о 21:55 Luca Ceresoli <luca.ceresoli@bootlin.com> пише:
> >>
> >> Hello Svyatoslav,
> >>
> >> On Thu Oct 2, 2025 at 8:20 AM CEST, Svyatoslav Ryhel wrote:
> >> >> > > > 12 represents amount of bits used per pixel, 8 for Y plane, 2 for U
> >> >> > > > plane and 2 for V plane, total is 12. "but explainable with a comment
> >> >> > > > and improve-able later" why then we cannot use 12 with a comment? this
> >> >> > > > is all arbitrary. Downstream is not wrong from this perspective, you
> >> >> > > > don't take into account that YUV420 is planar and it uses 3 planes a
> >> >> > > > whole Y plane and 1/4 of U and V which in total results in wigth + 2 *
> >> >> > > > 1/4 width which is width * 3/2
> >> >> > >
> >> >> > > Yes -- but AIUI, the only thing the bpp value is used for the bytesperline calculation. When we add the special case for planar formats, which doesn't use the bpp value, then the value 12 is never used anywhere. We should at least have a comment saying it is unused. (At that point, we could just hardcode the bpp values in the fmt_align function -- but I don't mind either way.)
> >> >> > >
> >> >> > https://ffmpeg.org/pipermail/ffmpeg-user/2023-June/056488.html
> >> >>
> >> >> I understand very well that for YUV420, each pixel has 12 bits of color information. But how many bits of color information each pixel has is not useful in the context of this driver. The number of bytes per line is not related to how many bits of color information each pixel has for planar formats.
> >> >
> >> > No, it has direct impact. This is how buffer size / image size is
> >> > calculated since we place each plane consecutive. And bytes per line
> >> > is used specifically in image size calculation. This is common part
> >> > with non-planar formats. Then since Tegra provides a dedicated
> >> > channels/buffers for each plane, configuration of planar format
> >> > includes an additional step with calculation for each plane.
> >>
> >> Sorry, I haven't followed the discussion in detail, but I tested you series
> >> on Tegra20 VIP and capture does not work, with a SIGSEGV in
> >> gstreamer. Bisecting pointed to this as the first commit where the issue
> >> happens.
> >>
> >> I compared the input and output values of tegra20_fmt_align() at this
> >> commit and at the previous one, and this is the result:
> >>
> >> before this patch with this patch
> >> At function entry:
> >> bpp 1 12
> >> pix->width 640 640
> >> pix->height 480 480
> >>
> >> On return:
> >> pix->bytesperline 640 960
> >> pix->sizeimage 460800 460800
> >>
> >> I hope these info will help.
> >
> > Which command did you use? I have tested with ffmpeg and
> > yuv422/yuv420p and it worked perfectly fine.
>
> I have a simple testing script that runs these commands, with
> VNODE="/dev/video0":
>
> v4l2-ctl -d ${VNODE} --set-ctrl horizontal_flip=1 --set-ctrl vertical_flip=1
>
> gst-launch-1.0 -ve v4l2src device=${VNODE} num-buffers=500 \
> ! video/x-raw,width=640,height=480,framerate=50/1,format=I420 \
> ! videorate drop-only=true skip-to-first=true \
> ! video/x-raw,framerate=50/4 \
> ! queue \
> ! avenc_mpeg4 \
> ! mp4mux \
> ! filesink location=/tmp/grab.mp4
>
> Luca
I can reproduce what you are observing. ok, I will drop this commit.
> --
> Luca Ceresoli, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
^ permalink raw reply [flat|nested] 50+ messages in thread
end of thread, other threads:[~2025-10-08 5:45 UTC | newest]
Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-25 15:16 [PATCH v3 00/22] tegra-video: add CSI support for Tegra20 and Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 01/22] clk: tegra: set CSUS as vi_sensor's gate for Tegra20, Tegra30 and Tegra114 Svyatoslav Ryhel
2025-10-01 4:02 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 02/22] dt-bindings: clock: tegra30: Add IDs for CSI pad clocks Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 03/22] clk: tegra30: add CSI pad clock gates Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 04/22] dt-bindings: display: tegra: document Tegra30 VI and VIP Svyatoslav Ryhel
2025-10-02 1:19 ` Rob Herring (Arm)
2025-09-25 15:16 ` [PATCH v3 05/22] staging: media: tegra-video: expand VI and VIP support to Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 06/22] staging: media: tegra-video: vi: adjust get_selection op check Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 07/22] staging: media: tegra-video: vi: add flip controls only if no source controls are provided Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 08/22] staging: media: tegra-video: csi: move CSI helpers to header Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 09/22] gpu: host1x: convert MIPI to use operation function pointers Svyatoslav Ryhel
2025-10-01 4:18 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 10/22] staging: media: tegra-video: vi: improve logic of source requesting Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 11/22] staging: media: tegra-video: csi: move avdd-dsi-csi-supply from VI to CSI Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 12/22] arm64: tegra: move avdd-dsi-csi-supply into CSI node Svyatoslav Ryhel
2025-10-01 4:27 ` Mikko Perttunen
2025-09-25 15:16 ` [PATCH v3 13/22] staging: media: tegra-video: tegra20: set correct maximum width and height Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 14/22] staging: media: tegra-video: tegra20: add support for second output of VI Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 15/22] staging: media: tegra-video: tegra20: simplify format align calculations Svyatoslav Ryhel
2025-10-01 4:38 ` Mikko Perttunen
2025-10-01 5:07 ` Svyatoslav Ryhel
2025-10-01 5:35 ` Svyatoslav Ryhel
2025-10-01 7:51 ` Mikko Perttunen
2025-10-01 7:59 ` Svyatoslav Ryhel
2025-10-02 4:00 ` Mikko Perttunen
2025-10-02 5:41 ` Svyatoslav Ryhel
2025-10-02 6:12 ` Mikko Perttunen
2025-10-02 6:20 ` Svyatoslav Ryhel
2025-10-06 18:54 ` Luca Ceresoli
2025-10-07 16:02 ` Svyatoslav Ryhel
2025-10-07 19:37 ` Luca Ceresoli
2025-10-08 5:44 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 16/22] staging: media: tegra-video: tegra20: set VI HW revision Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 17/22] staging: media: tegra-video: tegra20: increase maximum VI clock frequency Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 18/22] staging: media: tegra-video: tegra20: expand format support with RAW8/10 and YUV422 1X16 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 19/22] staging: media: tegra-video: tegra20: adjust luma buffer stride Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 20/22] dt-bindings: display: tegra: document Tegra20 and Tegra30 CSI Svyatoslav Ryhel
2025-10-02 1:52 ` Rob Herring
2025-10-02 5:14 ` Svyatoslav Ryhel
2025-10-06 20:31 ` Rob Herring
2025-10-07 5:13 ` Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 21/22] ARM: tegra: add CSI nodes for Tegra20 and Tegra30 Svyatoslav Ryhel
2025-09-25 15:16 ` [PATCH v3 22/22] staging: media: tegra-video: add CSI support " Svyatoslav Ryhel
2025-10-01 5:04 ` Mikko Perttunen
2025-10-01 5:15 ` Svyatoslav Ryhel
2025-10-01 6:38 ` Mikko Perttunen
2025-10-01 15:23 ` Svyatoslav Ryhel
2025-10-02 4:03 ` Mikko Perttunen
2025-10-02 17:49 ` Svyatoslav Ryhel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).