* [PATCH v6 01/11] PCI: rcar: Rename pcie-rcar.c to pcie-rcar-host.c
From: Lad Prabhakar @ 2020-04-02 19:38 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Mark Rutland, Geert Uytterhoeven,
Magnus Damm, Kishon Vijay Abraham I, Lorenzo Pieralisi,
Marek Vasut, Yoshihiro Shimoda, linux-pci
Cc: Catalin Marinas, Will Deacon, Arnd Bergmann, Greg Kroah-Hartman,
Andrew Murray, devicetree, linux-kernel, linux-arm-kernel,
linux-renesas-soc, Chris Paterson, Frank Rowand, Gustavo Pimentel,
Jingoo Han, Simon Horman, Shawn Lin, Tom Joseph, Heiko Stuebner,
linux-rockchip, Lad Prabhakar, Lad Prabhakar
In-Reply-To: <1585856319-4380-1-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com>
This commit renames pcie-rcar.c to pcie-rcar-host.c in preparation for
adding support for endpoint mode. CONFIG_PCIE_RCAR is kept so that arm64
defconfig change can be a separate patch.
With this patch both config options PCIE_RCAR and PCIE_RCAR_HOST will be
available but PCIE_RCAR internally selects PCIE_RCAR_HOST so that bisect
builds wont be affected.
Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
---
drivers/pci/controller/Kconfig | 10 ++++++++++
drivers/pci/controller/Makefile | 2 +-
.../pci/controller/{pcie-rcar.c => pcie-rcar-host.c} | 0
3 files changed, 11 insertions(+), 1 deletion(-)
rename drivers/pci/controller/{pcie-rcar.c => pcie-rcar-host.c} (100%)
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index af0f0bc11917..cfdc898450d0 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -58,8 +58,18 @@ config PCIE_RCAR
bool "Renesas R-Car PCIe controller"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_RCAR_HOST
help
Say Y here if you want PCIe controller support on R-Car SoCs.
+ This option will be removed after arm64 defconfig is updated.
+
+config PCIE_RCAR_HOST
+ bool "Renesas R-Car PCIe host controller"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on PCI_MSI_IRQ_DOMAIN
+ help
+ Say Y here if you want PCIe controller support on R-Car SoCs in host
+ mode.
config PCI_HOST_COMMON
bool
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 158c59771824..9dbccb5b24e1 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
-obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o
+obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar-host.o
obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
diff --git a/drivers/pci/controller/pcie-rcar.c b/drivers/pci/controller/pcie-rcar-host.c
similarity index 100%
rename from drivers/pci/controller/pcie-rcar.c
rename to drivers/pci/controller/pcie-rcar-host.c
--
2.20.1
^ permalink raw reply related
* [PATCH v6 00/11] Add support for PCIe controller to work in endpoint mode on R-Car SoCs
From: Lad Prabhakar @ 2020-04-02 19:38 UTC (permalink / raw)
To: Bjorn Helgaas, Rob Herring, Mark Rutland, Geert Uytterhoeven,
Magnus Damm, Kishon Vijay Abraham I, Lorenzo Pieralisi,
Marek Vasut, Yoshihiro Shimoda, linux-pci
Cc: Catalin Marinas, Will Deacon, Arnd Bergmann, Greg Kroah-Hartman,
Andrew Murray, devicetree, linux-kernel, linux-arm-kernel,
linux-renesas-soc, Chris Paterson, Frank Rowand, Gustavo Pimentel,
Jingoo Han, Simon Horman, Shawn Lin, Tom Joseph, Heiko Stuebner,
linux-rockchip, Lad Prabhakar, Lad Prabhakar
This patch series adds support for PCIe controller on rcar to work in
endpoint mode, this also extends the epf framework to handle base region
for mapping PCI address locally.
Note:
The cadence/rockchip/designware endpoint drivers are build tested only.
root@salvator-x:~# ./pcitest.sh
BAR tests
BAR0: OKAY
BAR1: NOT OKAY
BAR2: OKAY
BAR3: NOT OKAY
BAR4: OKAY
BAR5: NOT OKAY
Interrupt tests
SET IRQ TYPE TO LEGACY: OKAY
LEGACY IRQ: OKAY
SET IRQ TYPE TO MSI: OKAY
MSI1: OKAY
MSI2: OKAY
MSI3: OKAY
MSI4: OKAY
MSI5: OKAY
MSI6: OKAY
MSI7: OKAY
MSI8: OKAY
MSI9: OKAY
MSI10: OKAY
MSI11: OKAY
MSI12: OKAY
MSI13: OKAY
MSI14: OKAY
MSI15: OKAY
MSI16: OKAY
MSI17: NOT OKAY
MSI18: NOT OKAY
MSI19: NOT OKAY
MSI20: NOT OKAY
MSI21: NOT OKAY
MSI22: NOT OKAY
MSI23: NOT OKAY
MSI24: NOT OKAY
MSI25: NOT OKAY
MSI26: NOT OKAY
MSI27: NOT OKAY
MSI28: NOT OKAY
MSI29: NOT OKAY
MSI30: NOT OKAY
MSI31: NOT OKAY
MSI32: NOT OKAY
Read Tests
SET IRQ TYPE TO MSI: OKAY
READ ( 1 bytes): OKAY
READ ( 1024 bytes): OKAY
READ ( 1025 bytes): OKAY
READ (1024000 bytes): OKAY
READ (1024001 bytes): OKAY
Write Tests
WRITE ( 1 bytes): OKAY
WRITE ( 1024 bytes): OKAY
WRITE ( 1025 bytes): OKAY
WRITE (1024000 bytes): OKAY
WRITE (1024001 bytes): OKAY
Copy Tests
COPY ( 1 bytes): OKAY
COPY ( 1024 bytes): OKAY
COPY ( 1025 bytes): OKAY
COPY (1024000 bytes): OKAY
COPY (1024001 bytes): OKAY
Changes for v6:
1] Rebased patches on endpoint branch of https://git.kernel.org/pub/
scm/linux/kernel/git/lpieralisi/pci.git/
2] Fixed review comments from Shimoda-san
a] Made sure defconfig changes were in seprate patch
b] Created rcar_pcie_host/rcar_pcie_ep structures
c] Added pci-id for R8A774C0
d] Added entry in MAINTAINERS for dt-binding
e] Dropped unnecessary braces
3] Added support for msi.
Changes for v5:
1] Rebased patches on next branch of https://git.kernel.org/pub/scm/
linux/kernel/git/helgaas/pci.git
2] Fixed review comments reported by Kishon while fetching the matching
window in function pci_epc_get_matching_window()
3] Fixed review comments reported by Bjorn
a] Split patch up first patch so that its easier to review and incremental
b] Fixed typos
4] Included Reviewed tag from Rob for the dt-binding patch
5] Fixed issue reported by Nathan for assigning variable to itself
Changes for v4:
1] Fixed dtb_check error reported by Rob
2] Fixed review comments reported by Kishon
a] Dropped pci_epc_find_best_fit_window()
b] Fixed initializing mem ptr in __pci_epc_mem_init()
c] Dropped map_size from pci_epc_mem_window structure
Changes for v3:
1] Fixed review comments from Bjorn and Kishon.
3] Converted to DT schema
Changes for v2:
1] Fixed review comments from Biju for dt-bindings to include an example
for a tested platform.
2] Fixed review comments from Kishon to extend the features of outbound
regions in epf framework.
3] Added support to parse outbound-ranges in OF.
Lad Prabhakar (11):
PCI: rcar: Rename pcie-rcar.c to pcie-rcar-host.c
arm64: defconfig: enable CONFIG_PCIE_RCAR_HOST
PCI: drop PCIE_RCAR config option
PCI: rcar: Move shareable code to a common file
PCI: rcar: Fix calculating mask for PCIEPAMR register
PCI: endpoint: Add support to handle multiple base for mapping
outbound memory
dt-bindings: PCI: rcar: Add bindings for R-Car PCIe endpoint
controller
PCI: rcar: Add support for rcar PCIe controller in endpoint mode
PCI: Add Renesas R8A774C0 device ID
misc: pci_endpoint_test: Add Device ID for RZ/G2E PCIe controller
MAINTAINERS: Add file patterns for rcar PCI device tree bindings
.../devicetree/bindings/pci/rcar-pci-ep.yaml | 76 ++
MAINTAINERS | 1 +
arch/arm64/configs/defconfig | 2 +-
drivers/misc/pci_endpoint_test.c | 2 +
drivers/pci/controller/Kconfig | 15 +-
drivers/pci/controller/Makefile | 3 +-
.../pci/controller/cadence/pcie-cadence-ep.c | 3 +-
.../pci/controller/dwc/pcie-designware-ep.c | 16 +-
drivers/pci/controller/pcie-rcar-ep.c | 556 ++++++++
drivers/pci/controller/pcie-rcar-host.c | 1065 +++++++++++++++
drivers/pci/controller/pcie-rcar.c | 1206 +----------------
drivers/pci/controller/pcie-rcar.h | 140 ++
drivers/pci/controller/pcie-rockchip-ep.c | 2 +-
drivers/pci/endpoint/pci-epc-mem.c | 195 ++-
include/linux/pci-epc.h | 39 +-
include/linux/pci_ids.h | 1 +
16 files changed, 2068 insertions(+), 1254 deletions(-)
create mode 100644 Documentation/devicetree/bindings/pci/rcar-pci-ep.yaml
create mode 100644 drivers/pci/controller/pcie-rcar-ep.c
create mode 100644 drivers/pci/controller/pcie-rcar-host.c
create mode 100644 drivers/pci/controller/pcie-rcar.h
--
2.20.1
^ permalink raw reply
* [PATCH v5 9/9] arm64: dts: renesas: salvator: add a connection from adv748x codec (HDMI input) to the R-Car SoC
From: Alex Riesen @ 2020-04-02 18:35 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
As all known variants of the Salvator board have the HDMI decoder
chip (the ADV7482) connected to the SSI4 on R-Car SoC, the ADV7482
endpoint and the connection definitions are placed in the common board
file.
For the same reason, the CLK_C clock line and I2C configuration (similar
to the ak4613, on the same interface) are added into the common file.
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
--
v5: Add dummy ssi4 node to the rcar sound card in r8a77961, as the
devices (Salvator-X 2nd version with R-Car M3 W+) also reference
salvator-common.dtsi.
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
v2: Also add ssi4_ctrl pin group in the sound pins. The pins are
responsible for SCK4 (sample clock) WS4 and (word boundary input),
and are required for SSI audio input over I2S.
The adv748x shall provide its own implementation of the output clock
(MCLK), connected to the audio_clk_c line of the R-Car SoC.
If the frequency of the ADV748x MCLK were fixed, the clock
implementation were not necessary, but it does not seem so: the MCLK
depends on the value in a speed multiplier register and the input sample
rate (48kHz).
Remove audio clock C from the clocks of adv7482.
The clocks property of the video-receiver node lists the input
clocks of the device, which is quite the opposite from the
original intention: the adv7482 on Salvator X boards is a
provide of the MCLK clock for I2S audio output.
Remove old definition of &sound_card.dais and reduce size of changes
in the Salvator-X specific device tree source.
Declare video-receiver a clock producer, as the adv748x driver
implements the master clock used I2S audio output.
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
v2: The driver provides only MCLK clock, not the SCLK and LRCLK,
which are part of the I2S protocol.
Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
.../boot/dts/renesas/r8a77950-salvator-x.dts | 3 +-
arch/arm64/boot/dts/renesas/r8a77961.dtsi | 1 +
.../boot/dts/renesas/salvator-common.dtsi | 47 +++++++++++++++++--
3 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/renesas/r8a77950-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a77950-salvator-x.dts
index 2438825c9b22..e16c146808b6 100644
--- a/arch/arm64/boot/dts/renesas/r8a77950-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a77950-salvator-x.dts
@@ -146,7 +146,8 @@ &sata {
&sound_card {
dais = <&rsnd_port0 /* ak4613 */
&rsnd_port1 /* HDMI0 */
- &rsnd_port2>; /* HDMI1 */
+ &rsnd_port2 /* HDMI1 */
+ &rsnd_port3>; /* adv7482 hdmi-in */
};
&usb2_phy2 {
diff --git a/arch/arm64/boot/dts/renesas/r8a77961.dtsi b/arch/arm64/boot/dts/renesas/r8a77961.dtsi
index be3824bda632..b79907beaf31 100644
--- a/arch/arm64/boot/dts/renesas/r8a77961.dtsi
+++ b/arch/arm64/boot/dts/renesas/r8a77961.dtsi
@@ -861,6 +861,7 @@ rcar_sound,src {
rcar_sound,ssi {
ssi0: ssi-0 { };
ssi1: ssi-1 { };
+ ssi4: ssi-4 { };
};
};
diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
index 98bbcafc8c0d..ead7f8d7a929 100644
--- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi
+++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
@@ -460,7 +460,7 @@ pca9654: gpio@20 {
#gpio-cells = <2>;
};
- video-receiver@70 {
+ adv7482_hdmi_in: video-receiver@70 {
compatible = "adi,adv7482";
reg = <0x70 0x71 0x72 0x73 0x74 0x75
0x60 0x61 0x62 0x63 0x64 0x65>;
@@ -469,6 +469,7 @@ video-receiver@70 {
#address-cells = <1>;
#size-cells = <0>;
+ #clock-cells = <0>; /* the MCLK for I2S output */
interrupt-parent = <&gpio6>;
interrupt-names = "intrq1", "intrq2";
@@ -510,6 +511,15 @@ adv7482_txb: endpoint {
remote-endpoint = <&csi20_in>;
};
};
+
+ port@c {
+ reg = <12>;
+
+ adv7482_i2s: endpoint {
+ remote-endpoint = <&rsnd_endpoint3>;
+ system-clock-direction-out;
+ };
+ };
};
csa_vdd: adc@7c {
@@ -684,7 +694,8 @@ sdhi3_pins_uhs: sd3_uhs {
};
sound_pins: sound {
- groups = "ssi01239_ctrl", "ssi0_data", "ssi1_data_a";
+ groups = "ssi01239_ctrl", "ssi0_data", "ssi1_data_a",
+ "ssi4_data", "ssi4_ctrl";
function = "ssi";
};
@@ -733,8 +744,8 @@ &rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
pinctrl-names = "default";
- /* Single DAI */
- #sound-dai-cells = <0>;
+ /* multi DAI */
+ #sound-dai-cells = <1>;
/* audio_clkout0/1/2/3 */
#clock-cells = <1>;
@@ -758,8 +769,19 @@ &rcar_sound {
<&cpg CPG_MOD 1020>, <&cpg CPG_MOD 1021>,
<&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>,
<&audio_clk_a>, <&cs2000>,
- <&audio_clk_c>,
+ <&adv7482_hdmi_in>,
<&cpg CPG_CORE CPG_AUDIO_CLK_I>;
+ clock-names = "ssi-all",
+ "ssi.9", "ssi.8", "ssi.7", "ssi.6",
+ "ssi.5", "ssi.4", "ssi.3", "ssi.2",
+ "ssi.1", "ssi.0",
+ "src.9", "src.8", "src.7", "src.6",
+ "src.5", "src.4", "src.3", "src.2",
+ "src.1", "src.0",
+ "mix.1", "mix.0",
+ "ctu.1", "ctu.0",
+ "dvc.0", "dvc.1",
+ "clk_a", "clk_b", "clk_c", "clk_i";
ports {
#address-cells = <1>;
@@ -777,6 +799,21 @@ rsnd_endpoint0: endpoint {
capture = <&ssi1 &src1 &dvc1>;
};
};
+ rsnd_port3: port@3 {
+ reg = <3>;
+ rsnd_endpoint3: endpoint {
+ remote-endpoint = <&adv7482_i2s>;
+
+ dai-tdm-slot-num = <8>;
+ dai-tdm-slot-width = <32>;
+ dai-format = "left_j";
+ mclk-fs = <256>;
+ bitclock-master = <&adv7482_i2s>;
+ frame-master = <&adv7482_i2s>;
+
+ capture = <&ssi4>;
+ };
+ };
};
};
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* Re: [PATCH v6 2/2] iio: adc: Add MAX1241 driver
From: Andy Shevchenko @ 2020-04-02 19:02 UTC (permalink / raw)
To: Lars-Peter Clausen
Cc: Alexandru Lazar, linux-iio, devicetree, Jonathan Cameron,
Hartmut Knaack, Peter Meerwald, Rob Herring, Mark Rutland,
Alexandru Ardelean
In-Reply-To: <4431cacc-4af6-a497-5850-20dfceb588c1@metafoo.de>
On Thu, Apr 2, 2020 at 9:55 PM Lars-Peter Clausen <lars@metafoo.de> wrote:
> On 4/2/20 8:45 PM, Andy Shevchenko wrote:
> > On Wed, Apr 1, 2020 at 9:47 PM Alexandru Lazar <alazar@startmail.com> wrote:
> >> Add driver for the Maxim MAX1241 12-bit, single-channel ADC.
> >>
> > Perhaps put data sheet link as Datasheet: tag here?
> >
> >> Reviewed-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> >> Signed-off-by: Alexandru Lazar <alazar@startmail.com>
> > ...
> >
> >> + indio_dev->name = spi_get_device_id(spi)->name;
> > Shouldn't be rather part number?
> > Jonathan?
>
> This is the part number as defined in the spi_device id table :)
Okay!
> +static const struct spi_device_id max1241_id[] = {
> + { "max1241", max1241 },
> + {}
> +};
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v5 8/9] dt-bindings: adv748x: add information about serial audio interface (I2S/TDM)
From: Alex Riesen @ 2020-04-02 18:35 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
As the driver has some support for the audio interface of the device,
the bindings file should mention it.
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
--
v3: remove optionality off MCLK clock cell to ensure the description
matches the hardware no matter if the line is connected.
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
---
.../devicetree/bindings/media/i2c/adv748x.txt | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/i2c/adv748x.txt b/Documentation/devicetree/bindings/media/i2c/adv748x.txt
index 4f91686e54a6..50a753189b81 100644
--- a/Documentation/devicetree/bindings/media/i2c/adv748x.txt
+++ b/Documentation/devicetree/bindings/media/i2c/adv748x.txt
@@ -2,7 +2,9 @@
The ADV7481 and ADV7482 are multi format video decoders with an integrated
HDMI receiver. They can output CSI-2 on two independent outputs TXA and TXB
-from three input sources HDMI, analog and TTL.
+from three input sources HDMI, analog and TTL. There is also support for an
+I2S-compatible interface connected to the audio processor of the HDMI decoder.
+The interface has TDM capability (8 slots, 32 bits, left or right justified).
Required Properties:
@@ -16,6 +18,8 @@ Required Properties:
slave device on the I2C bus. The main address is mandatory, others are
optional and remain at default values if not specified.
+ - #clock-cells: must be <0>
+
Optional Properties:
- interrupt-names: Should specify the interrupts as "intrq1", "intrq2" and/or
@@ -47,6 +51,7 @@ are numbered as follows.
TTL sink 9
TXA source 10
TXB source 11
+ I2S source 12
The digital output port nodes, when present, shall contain at least one
endpoint. Each of those endpoints shall contain the data-lanes property as
@@ -72,6 +77,7 @@ Example:
#address-cells = <1>;
#size-cells = <0>;
+ #clock-cells = <0>;
interrupt-parent = <&gpio6>;
interrupt-names = "intrq1", "intrq2";
@@ -113,4 +119,12 @@ Example:
remote-endpoint = <&csi20_in>;
};
};
+
+ port@c {
+ reg = <12>;
+
+ adv7482_i2s: endpoint {
+ remote-endpoint = <&i2s_in>;
+ };
+ };
};
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 7/9] media: adv748x: only activate DAI if it is described in device tree
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
To avoid setting it up even if the hardware is not actually connected
to anything physically.
Besides, the bindings explicitly notes that port definitions are
"optional if they are not connected to anything at the hardware level".
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
---
drivers/media/i2c/adv748x/adv748x-dai.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/media/i2c/adv748x/adv748x-dai.c b/drivers/media/i2c/adv748x/adv748x-dai.c
index 185f78023e91..f9cc47fa9ad1 100644
--- a/drivers/media/i2c/adv748x/adv748x-dai.c
+++ b/drivers/media/i2c/adv748x/adv748x-dai.c
@@ -216,6 +216,11 @@ int adv748x_dai_init(struct adv748x_dai *dai)
int ret;
struct adv748x_state *state = adv748x_dai_to_state(dai);
+ if (!state->endpoints[ADV748X_PORT_I2S]) {
+ adv_info(state, "no I2S port, DAI disabled\n");
+ ret = 0;
+ goto fail;
+ }
dai->mclk_name = kasprintf(GFP_KERNEL, "%s.%s-i2s-mclk",
state->dev->driver->name,
dev_name(state->dev));
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* Re: [PATCH v6 2/2] iio: adc: Add MAX1241 driver
From: Lars-Peter Clausen @ 2020-04-02 18:55 UTC (permalink / raw)
To: Andy Shevchenko, Alexandru Lazar
Cc: linux-iio, devicetree, Jonathan Cameron, Hartmut Knaack,
Peter Meerwald, Rob Herring, Mark Rutland, Alexandru Ardelean
In-Reply-To: <CAHp75VffpBYh+5xrWeeJJH8gRmJqT9ya5eQFedWi190_=p8HKQ@mail.gmail.com>
On 4/2/20 8:45 PM, Andy Shevchenko wrote:
> On Wed, Apr 1, 2020 at 9:47 PM Alexandru Lazar <alazar@startmail.com> wrote:
>> Add driver for the Maxim MAX1241 12-bit, single-channel ADC.
>>
> Perhaps put data sheet link as Datasheet: tag here?
>
>> Reviewed-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
>> Signed-off-by: Alexandru Lazar <alazar@startmail.com>
> ...
>
>> + indio_dev->name = spi_get_device_id(spi)->name;
> Shouldn't be rather part number?
> Jonathan?
This is the part number as defined in the spi_device id table :)
+static const struct spi_device_id max1241_id[] = {
+ { "max1241", max1241 },
+ {}
+};
- Lars
^ permalink raw reply
* [PATCH v5 6/9] media: adv748x: prepare/enable mclk when the audio is used
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
As there is nothing else (the consumers are supposed to do that) which
enables the clock, do it in the driver.
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
--
v3: added
---
drivers/media/i2c/adv748x/adv748x-dai.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/media/i2c/adv748x/adv748x-dai.c b/drivers/media/i2c/adv748x/adv748x-dai.c
index c9191f8f1ca8..185f78023e91 100644
--- a/drivers/media/i2c/adv748x/adv748x-dai.c
+++ b/drivers/media/i2c/adv748x/adv748x-dai.c
@@ -117,11 +117,22 @@ static int adv748x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adv748x_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
{
+ int ret;
struct adv748x_state *state = state_of(dai);
if (sub->stream != SNDRV_PCM_STREAM_CAPTURE)
return -EINVAL;
- return set_audio_pads_state(state, 1);
+ ret = set_audio_pads_state(state, 1);
+ if (ret)
+ goto fail;
+ ret = clk_prepare_enable(mclk_of(state));
+ if (ret)
+ goto fail_pwdn;
+ return 0;
+fail_pwdn:
+ set_audio_pads_state(state, 0);
+fail:
+ return ret;
}
static int adv748x_dai_hw_params(struct snd_pcm_substream *sub,
@@ -174,6 +185,7 @@ static void adv748x_dai_shutdown(struct snd_pcm_substream *sub, struct snd_soc_d
{
struct adv748x_state *state = state_of(dai);
+ clk_disable_unprepare(mclk_of(state));
set_audio_pads_state(state, 0);
}
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 5/9] media: adv748x: add support for HDMI audio
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc,
linux-clk
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
This adds an implemention of SoC DAI driver which provides access to the
I2S port of the device.
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
--
v3: fix clock registration in case of multiple adv748x devices
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
v4: use clk_hw instead of clk
Suggested-by: Stephen Boyd <sboyd@kernel.org>
v4: use const for capture snd_soc_pcm_stream instance
Suggested-by: Stephen Boyd <sboyd@kernel.org>
---
drivers/media/i2c/adv748x/Makefile | 3 +-
drivers/media/i2c/adv748x/adv748x-core.c | 9 +-
drivers/media/i2c/adv748x/adv748x-dai.c | 261 +++++++++++++++++++++++
drivers/media/i2c/adv748x/adv748x.h | 17 +-
4 files changed, 287 insertions(+), 3 deletions(-)
create mode 100644 drivers/media/i2c/adv748x/adv748x-dai.c
diff --git a/drivers/media/i2c/adv748x/Makefile b/drivers/media/i2c/adv748x/Makefile
index 93844f14cb10..6e7a302ef199 100644
--- a/drivers/media/i2c/adv748x/Makefile
+++ b/drivers/media/i2c/adv748x/Makefile
@@ -3,6 +3,7 @@ adv748x-objs := \
adv748x-afe.o \
adv748x-core.o \
adv748x-csi2.o \
- adv748x-hdmi.o
+ adv748x-hdmi.o \
+ adv748x-dai.o
obj-$(CONFIG_VIDEO_ADV748X) += adv748x.o
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 8580e6624276..3513ca138e53 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -765,8 +765,14 @@ static int adv748x_probe(struct i2c_client *client)
goto err_cleanup_txa;
}
+ ret = adv748x_dai_init(&state->dai);
+ if (ret) {
+ adv_err(state, "Failed to probe DAI\n");
+ goto err_cleanup_txb;
+ }
return 0;
-
+err_cleanup_txb:
+ adv748x_csi2_cleanup(&state->txb);
err_cleanup_txa:
adv748x_csi2_cleanup(&state->txa);
err_cleanup_afe:
@@ -787,6 +793,7 @@ static int adv748x_remove(struct i2c_client *client)
{
struct adv748x_state *state = i2c_get_clientdata(client);
+ adv748x_dai_cleanup(&state->dai);
adv748x_afe_cleanup(&state->afe);
adv748x_hdmi_cleanup(&state->hdmi);
diff --git a/drivers/media/i2c/adv748x/adv748x-dai.c b/drivers/media/i2c/adv748x/adv748x-dai.c
new file mode 100644
index 000000000000..c9191f8f1ca8
--- /dev/null
+++ b/drivers/media/i2c/adv748x/adv748x-dai.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Analog Devices ADV748X HDMI receiver with AFE
+ * The implementation of DAI.
+ */
+
+#include "adv748x.h"
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <sound/pcm_params.h>
+
+#define state_of(soc_dai) \
+ adv748x_dai_to_state(container_of((soc_dai)->driver, \
+ struct adv748x_dai, drv))
+#define mclk_of(state) ((state)->dai.mclk_hw->clk)
+
+static const char ADV748X_DAI_NAME[] = "adv748x-i2s";
+
+static int set_audio_pads_state(struct adv748x_state *state, int on)
+{
+ return io_clrset(state, ADV748X_IO_PAD_CONTROLS,
+ ADV748X_IO_PAD_CONTROLS_TRI_AUD |
+ ADV748X_IO_PAD_CONTROLS_PDN_AUD,
+ on ? 0 : 0xff);
+}
+
+static int set_dpll_mclk_fs(struct adv748x_state *state, int fs)
+{
+ return dpll_clrset(state, ADV748X_DPLL_MCLK_FS,
+ ADV748X_DPLL_MCLK_FS_N_MASK, (fs / 128) - 1);
+}
+
+static int set_i2s_format(struct adv748x_state *state, uint outmode,
+ uint bitwidth)
+{
+ return hdmi_clrset(state, ADV748X_HDMI_I2S,
+ ADV748X_HDMI_I2SBITWIDTH_MASK |
+ ADV748X_HDMI_I2SOUTMODE_MASK,
+ (outmode << ADV748X_HDMI_I2SOUTMODE_SHIFT) |
+ bitwidth);
+}
+
+static int set_i2s_tdm_mode(struct adv748x_state *state, int is_tdm)
+{
+ int ret;
+
+ ret = hdmi_clrset(state, ADV748X_HDMI_AUDIO_MUTE_SPEED,
+ ADV748X_MAN_AUDIO_DL_BYPASS |
+ ADV748X_AUDIO_DELAY_LINE_BYPASS,
+ is_tdm ? 0xff : 0);
+ if (ret < 0)
+ return ret;
+ ret = hdmi_clrset(state, ADV748X_HDMI_REG_6D,
+ ADV748X_I2S_TDM_MODE_ENABLE,
+ is_tdm ? 0xff : 0);
+ return ret;
+}
+
+static int set_audio_mute(struct adv748x_state *state, int enable)
+{
+ return hdmi_clrset(state, ADV748X_HDMI_MUTE_CTRL,
+ ADV748X_HDMI_MUTE_CTRL_MUTE_AUDIO,
+ enable ? 0xff : 0);
+}
+
+static int adv748x_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct adv748x_state *state = state_of(dai);
+
+ /* currently supporting only one fixed rate clock */
+ if (clk_id != 0 || freq != clk_get_rate(mclk_of(state))) {
+ dev_err(dai->dev, "invalid clock (%d) or frequency (%u, dir %d)\n",
+ clk_id, freq, dir);
+ return -EINVAL;
+ }
+ state->dai.freq = freq;
+ return 0;
+}
+
+static int adv748x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct adv748x_state *state = state_of(dai);
+ int ret = 0;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM) {
+ dev_err(dai->dev, "only I2S master clock mode supported\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAI_FORMAT_I2S:
+ state->dai.tdm = 0;
+ state->dai.fmt = ADV748X_I2SOUTMODE_I2S;
+ break;
+ case SND_SOC_DAI_FORMAT_RIGHT_J:
+ state->dai.tdm = 1;
+ state->dai.fmt = ADV748X_I2SOUTMODE_RIGHT_J;
+ break;
+ case SND_SOC_DAI_FORMAT_LEFT_J:
+ state->dai.tdm = 1;
+ state->dai.fmt = ADV748X_I2SOUTMODE_LEFT_J;
+ break;
+ default:
+ dev_err(dai->dev, "only i2s, left_j and right_j supported\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+ dev_err(dai->dev, "only normal bit clock + frame supported\n");
+ ret = -EINVAL;
+ }
+done:
+ return ret;
+}
+
+static int adv748x_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+ struct adv748x_state *state = state_of(dai);
+
+ if (sub->stream != SNDRV_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+ return set_audio_pads_state(state, 1);
+}
+
+static int adv748x_dai_hw_params(struct snd_pcm_substream *sub,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+ struct adv748x_state *state = state_of(dai);
+ uint fs = state->dai.freq / params_rate(params);
+
+ dev_dbg(dai->dev, "dai %s substream %s rate=%u (fs=%u), channels=%u sample width=%u(%u)\n",
+ dai->name, sub->name,
+ params_rate(params), fs,
+ params_channels(params),
+ params_width(params),
+ params_physical_width(params));
+ switch (fs) {
+ case 128:
+ case 256:
+ case 384:
+ case 512:
+ case 640:
+ case 768:
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(dai->dev, "invalid clock frequency (%u) or rate (%u)\n",
+ state->dai.freq, params_rate(params));
+ goto done;
+ }
+ ret = set_dpll_mclk_fs(state, fs);
+ if (ret)
+ goto done;
+ ret = set_i2s_tdm_mode(state, state->dai.tdm);
+ if (ret)
+ goto done;
+ ret = set_i2s_format(state, state->dai.fmt, params_width(params));
+done:
+ return ret;
+}
+
+static int adv748x_dai_mute_stream(struct snd_soc_dai *dai, int mute, int dir)
+{
+ struct adv748x_state *state = state_of(dai);
+
+ return set_audio_mute(state, mute);
+}
+
+static void adv748x_dai_shutdown(struct snd_pcm_substream *sub, struct snd_soc_dai *dai)
+{
+ struct adv748x_state *state = state_of(dai);
+
+ set_audio_pads_state(state, 0);
+}
+
+static const struct snd_soc_dai_ops adv748x_dai_ops = {
+ .set_sysclk = adv748x_dai_set_sysclk,
+ .set_fmt = adv748x_dai_set_fmt,
+ .startup = adv748x_dai_startup,
+ .hw_params = adv748x_dai_hw_params,
+ .mute_stream = adv748x_dai_mute_stream,
+ .shutdown = adv748x_dai_shutdown,
+};
+
+static int adv748x_of_xlate_dai_name(struct snd_soc_component *component,
+ struct of_phandle_args *args,
+ const char **dai_name)
+{
+ if (dai_name)
+ *dai_name = ADV748X_DAI_NAME;
+ return 0;
+}
+
+static const struct snd_soc_component_driver adv748x_codec = {
+ .of_xlate_dai_name = adv748x_of_xlate_dai_name,
+};
+
+int adv748x_dai_init(struct adv748x_dai *dai)
+{
+ int ret;
+ struct adv748x_state *state = adv748x_dai_to_state(dai);
+
+ dai->mclk_name = kasprintf(GFP_KERNEL, "%s.%s-i2s-mclk",
+ state->dev->driver->name,
+ dev_name(state->dev));
+ if (!dai->mclk_name) {
+ ret = -ENOMEM;
+ adv_err(state, "No memory for MCLK\n");
+ goto fail;
+ }
+ dai->mclk_hw = clk_hw_register_fixed_rate(state->dev, dai->mclk_name,
+ NULL, 0, 12288000);
+ if (IS_ERR(dai->mclk_hw)) {
+ ret = PTR_ERR(dai->mclk_hw);
+ adv_err(state, "Failed to register MCLK (%d)\n", ret);
+ goto fail;
+ }
+ ret = of_clk_add_hw_provider(state->dev->of_node, of_clk_hw_simple_get,
+ dai->mclk_hw->clk);
+ if (ret < 0) {
+ adv_err(state, "Failed to add MCLK provider (%d)\n", ret);
+ goto unreg_mclk;
+ }
+ dai->drv.name = ADV748X_DAI_NAME;
+ dai->drv.ops = &adv748x_dai_ops;
+ dai->drv.capture = (const struct snd_soc_pcm_stream){
+ .stream_name = "Capture",
+ .channels_min = 8,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE,
+ };
+
+ ret = devm_snd_soc_register_component(state->dev, &adv748x_codec,
+ &dai->drv, 1);
+ if (ret < 0) {
+ adv_err(state, "Failed to register the codec (%d)\n", ret);
+ goto cleanup_mclk;
+ }
+ return 0;
+
+cleanup_mclk:
+ of_clk_del_provider(state->dev->of_node);
+unreg_mclk:
+ clk_hw_unregister_fixed_rate(dai->mclk_hw);
+fail:
+ return ret;
+}
+
+void adv748x_dai_cleanup(struct adv748x_dai *dai)
+{
+ struct adv748x_state *state = adv748x_dai_to_state(dai);
+
+ of_clk_del_provider(state->dev->of_node);
+ clk_hw_unregister_fixed_rate(dai->mclk_hw);
+ kfree(dai->mclk_name);
+}
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 1a1ea70086c6..454f97ff7b54 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -19,6 +19,7 @@
*/
#include <linux/i2c.h>
+#include <sound/soc.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -63,7 +64,8 @@ enum adv748x_ports {
ADV748X_PORT_TTL = 9,
ADV748X_PORT_TXA = 10,
ADV748X_PORT_TXB = 11,
- ADV748X_PORT_MAX = 12,
+ ADV748X_PORT_I2S = 12,
+ ADV748X_PORT_MAX = 13,
};
enum adv748x_csi2_pads {
@@ -166,6 +168,13 @@ struct adv748x_afe {
container_of(ctrl->handler, struct adv748x_afe, ctrl_hdl)
#define adv748x_sd_to_afe(sd) container_of(sd, struct adv748x_afe, sd)
+struct adv748x_dai {
+ struct snd_soc_dai_driver drv;
+ struct clk_hw *mclk_hw;
+ char *mclk_name;
+ unsigned int freq, fmt, tdm;
+};
+
/**
* struct adv748x_state - State of ADV748X
* @dev: (OF) device
@@ -182,6 +191,7 @@ struct adv748x_afe {
* @afe: state of AFE receiver context
* @txa: state of TXA transmitter context
* @txb: state of TXB transmitter context
+ * @mclk: MCLK clock of the I2S port
*/
struct adv748x_state {
struct device *dev;
@@ -197,10 +207,12 @@ struct adv748x_state {
struct adv748x_afe afe;
struct adv748x_csi2 txa;
struct adv748x_csi2 txb;
+ struct adv748x_dai dai;
};
#define adv748x_hdmi_to_state(h) container_of(h, struct adv748x_state, hdmi)
#define adv748x_afe_to_state(a) container_of(a, struct adv748x_state, afe)
+#define adv748x_dai_to_state(p) container_of(p, struct adv748x_state, dai)
#define adv_err(a, fmt, arg...) dev_err(a->dev, fmt, ##arg)
#define adv_info(a, fmt, arg...) dev_info(a->dev, fmt, ##arg)
@@ -484,4 +496,7 @@ int adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate);
int adv748x_hdmi_init(struct adv748x_hdmi *hdmi);
void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi);
+int adv748x_dai_init(struct adv748x_dai *);
+void adv748x_dai_cleanup(struct adv748x_dai *);
+
#endif /* _ADV748X_H_ */
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 4/9] media: adv748x: add definitions for audio output related registers
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
---
drivers/media/i2c/adv748x/adv748x.h | 32 +++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 0a9d78c2870b..1a1ea70086c6 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -226,6 +226,11 @@ struct adv748x_state {
#define ADV748X_IO_VID_STD 0x05
+#define ADV748X_IO_PAD_CONTROLS 0x0e
+#define ADV748X_IO_PAD_CONTROLS_TRI_AUD BIT(5)
+#define ADV748X_IO_PAD_CONTROLS_PDN_AUD BIT(1)
+#define ADV748X_IO_PAD_CONTROLS1 0x1d
+
#define ADV748X_IO_10 0x10 /* io_reg_10 */
#define ADV748X_IO_10_CSI4_EN BIT(7)
#define ADV748X_IO_10_CSI1_EN BIT(6)
@@ -248,7 +253,21 @@ struct adv748x_state {
#define ADV748X_IO_REG_FF 0xff
#define ADV748X_IO_REG_FF_MAIN_RESET 0xff
+/* DPLL Map */
+#define ADV748X_DPLL_MCLK_FS 0xb5
+#define ADV748X_DPLL_MCLK_FS_N_MASK GENMASK(2, 0)
+
/* HDMI RX Map */
+#define ADV748X_HDMI_I2S 0x03 /* I2S mode and width */
+#define ADV748X_HDMI_I2SBITWIDTH_MASK GENMASK(4, 0)
+#define ADV748X_HDMI_I2SOUTMODE_SHIFT 5
+#define ADV748X_HDMI_I2SOUTMODE_MASK \
+ GENMASK(6, ADV748X_HDMI_I2SOUTMODE_SHIFT)
+#define ADV748X_I2SOUTMODE_I2S 0
+#define ADV748X_I2SOUTMODE_RIGHT_J 1
+#define ADV748X_I2SOUTMODE_LEFT_J 2
+#define ADV748X_I2SOUTMODE_SPDIF 3
+
#define ADV748X_HDMI_LW1 0x07 /* line width_1 */
#define ADV748X_HDMI_LW1_VERT_FILTER BIT(7)
#define ADV748X_HDMI_LW1_DE_REGEN BIT(5)
@@ -260,6 +279,16 @@ struct adv748x_state {
#define ADV748X_HDMI_F1H1 0x0b /* field1 height_1 */
#define ADV748X_HDMI_F1H1_INTERLACED BIT(5)
+#define ADV748X_HDMI_MUTE_CTRL 0x1a
+#define ADV748X_HDMI_MUTE_CTRL_MUTE_AUDIO BIT(4)
+#define ADV748X_HDMI_MUTE_CTRL_WAIT_UNMUTE_MASK GENMASK(3, 1)
+#define ADV748X_HDMI_MUTE_CTRL_NOT_AUTO_UNMUTE BIT(0)
+
+#define ADV748X_HDMI_AUDIO_MUTE_SPEED 0x0f
+#define ADV748X_HDMI_AUDIO_MUTE_SPEED_MASK GENMASK(4, 0)
+#define ADV748X_MAN_AUDIO_DL_BYPASS BIT(7)
+#define ADV748X_AUDIO_DELAY_LINE_BYPASS BIT(6)
+
#define ADV748X_HDMI_HFRONT_PORCH 0x20 /* hsync_front_porch_1 */
#define ADV748X_HDMI_HFRONT_PORCH_MASK 0x1fff
@@ -281,6 +310,9 @@ struct adv748x_state {
#define ADV748X_HDMI_TMDS_1 0x51 /* hdmi_reg_51 */
#define ADV748X_HDMI_TMDS_2 0x52 /* hdmi_reg_52 */
+#define ADV748X_HDMI_REG_6D 0x6d /* hdmi_reg_6d */
+#define ADV748X_I2S_TDM_MODE_ENABLE BIT(7)
+
/* HDMI RX Repeater Map */
#define ADV748X_REPEATER_EDID_SZ 0x70 /* primary_edid_size */
#define ADV748X_REPEATER_EDID_SZ_SHIFT 4
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* Re: [PATCH v6 2/2] iio: adc: Add MAX1241 driver
From: Andy Shevchenko @ 2020-04-02 18:45 UTC (permalink / raw)
To: Alexandru Lazar
Cc: linux-iio, devicetree, Jonathan Cameron, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald, Rob Herring, Mark Rutland,
Alexandru Ardelean
In-Reply-To: <20200401185138.10673-3-alazar@startmail.com>
On Wed, Apr 1, 2020 at 9:47 PM Alexandru Lazar <alazar@startmail.com> wrote:
>
> Add driver for the Maxim MAX1241 12-bit, single-channel ADC.
>
Perhaps put data sheet link as Datasheet: tag here?
> Reviewed-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> Signed-off-by: Alexandru Lazar <alazar@startmail.com>
...
> + indio_dev->name = spi_get_device_id(spi)->name;
Shouldn't be rather part number?
Jonathan?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v5 3/9] media: adv748x: reduce amount of code for bitwise modifications of device registers
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
The regmap provides a convenient utility for this.
The hdmi_* and dpll_* register modification macros added for symmetry
with the existing operations (io_*, sdp_*).
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
--
v3: remove _update name in favor of existing _clrset
---
drivers/media/i2c/adv748x/adv748x-core.c | 6 ++++++
drivers/media/i2c/adv748x/adv748x.h | 14 +++++++++++---
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 5c59aad319d1..8580e6624276 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -133,6 +133,12 @@ static int adv748x_write_check(struct adv748x_state *state, u8 page, u8 reg,
return *error;
}
+int adv748x_update_bits(struct adv748x_state *state, u8 page, u8 reg, u8 mask,
+ u8 value)
+{
+ return regmap_update_bits(state->regmap[page], reg, mask, value);
+}
+
/* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
* size to one or more registers.
*
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index 09aab4138c3f..0a9d78c2870b 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -393,25 +393,33 @@ int adv748x_write(struct adv748x_state *state, u8 page, u8 reg, u8 value);
int adv748x_write_block(struct adv748x_state *state, int client_page,
unsigned int init_reg, const void *val,
size_t val_len);
+int adv748x_update_bits(struct adv748x_state *state, u8 page, u8 reg,
+ u8 mask, u8 value);
#define io_read(s, r) adv748x_read(s, ADV748X_PAGE_IO, r)
#define io_write(s, r, v) adv748x_write(s, ADV748X_PAGE_IO, r, v)
-#define io_clrset(s, r, m, v) io_write(s, r, (io_read(s, r) & ~(m)) | (v))
+#define io_clrset(s, r, m, v) adv748x_update_bits(s, ADV748X_PAGE_IO, r, m, v)
#define hdmi_read(s, r) adv748x_read(s, ADV748X_PAGE_HDMI, r)
#define hdmi_read16(s, r, m) (((hdmi_read(s, r) << 8) | hdmi_read(s, (r)+1)) & (m))
#define hdmi_write(s, r, v) adv748x_write(s, ADV748X_PAGE_HDMI, r, v)
+#define hdmi_clrset(s, r, m, v) \
+ adv748x_update_bits(s, ADV748X_PAGE_HDMI, r, m, v)
+
+#define dpll_read(s, r) adv748x_read(s, ADV748X_PAGE_DPLL, r)
+#define dpll_clrset(s, r, m, v) \
+ adv748x_update_bits(s, ADV748X_PAGE_DPLL, r, m, v)
#define repeater_read(s, r) adv748x_read(s, ADV748X_PAGE_REPEATER, r)
#define repeater_write(s, r, v) adv748x_write(s, ADV748X_PAGE_REPEATER, r, v)
#define sdp_read(s, r) adv748x_read(s, ADV748X_PAGE_SDP, r)
#define sdp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_SDP, r, v)
-#define sdp_clrset(s, r, m, v) sdp_write(s, r, (sdp_read(s, r) & ~(m)) | (v))
+#define sdp_clrset(s, r, m, v) adv748x_update_bits(s, ADV748X_PAGE_SDP, r, m, v)
#define cp_read(s, r) adv748x_read(s, ADV748X_PAGE_CP, r)
#define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v)
-#define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~(m)) | (v))
+#define cp_clrset(s, r, m, v) adv748x_update_bits(s, ADV748X_PAGE_CP, r, m, v)
#define tx_read(t, r) adv748x_read(t->state, t->page, r)
#define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v)
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 2/9] media: adv748x: include everything adv748x.h needs into the file
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
To follow the established practice of not depending on others to
pull everything in. While at it, make sure it stays like this.
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/i2c/adv748x/adv748x-afe.c | 6 ++----
drivers/media/i2c/adv748x/adv748x-core.c | 6 ++----
drivers/media/i2c/adv748x/adv748x-csi2.c | 6 ++----
drivers/media/i2c/adv748x/adv748x-hdmi.c | 6 ++----
drivers/media/i2c/adv748x/adv748x.h | 2 ++
5 files changed, 10 insertions(+), 16 deletions(-)
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index dbbb1e4d6363..5a25d1fbe25f 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -6,18 +6,16 @@
* Copyright (C) 2017 Renesas Electronics Corp.
*/
+#include "adv748x.h"
+
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ioctl.h>
-#include "adv748x.h"
-
/* -----------------------------------------------------------------------------
* SDP
*/
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index c3fb113cef62..5c59aad319d1 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -10,6 +10,8 @@
* Kieran Bingham <kieran.bingham@ideasonboard.com>
*/
+#include "adv748x.h"
+
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/i2c.h>
@@ -20,14 +22,10 @@
#include <linux/slab.h>
#include <linux/v4l2-dv-timings.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-ioctl.h>
-#include "adv748x.h"
-
/* -----------------------------------------------------------------------------
* Register manipulation
*/
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index c43ce5d78723..c00d4f347d95 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -5,15 +5,13 @@
* Copyright (C) 2017 Renesas Electronics Corp.
*/
+#include "adv748x.h"
+
#include <linux/module.h>
#include <linux/mutex.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include "adv748x.h"
-
static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx,
unsigned int vc)
{
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index c557f8fdf11a..f598acec3b5c 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -5,18 +5,16 @@
* Copyright (C) 2017 Renesas Electronics Corp.
*/
+#include "adv748x.h"
+
#include <linux/module.h>
#include <linux/mutex.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ioctl.h>
#include <uapi/linux/v4l2-dv-timings.h>
-#include "adv748x.h"
-
/* -----------------------------------------------------------------------------
* HDMI and CP
*/
diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h
index fccb388ce179..09aab4138c3f 100644
--- a/drivers/media/i2c/adv748x/adv748x.h
+++ b/drivers/media/i2c/adv748x/adv748x.h
@@ -19,6 +19,8 @@
*/
#include <linux/i2c.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
#ifndef _ADV748X_H_
#define _ADV748X_H_
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 1/9] media: adv748x: fix end-of-line terminators in diagnostic statements
From: Alex Riesen @ 2020-04-02 18:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
In-Reply-To: <cover.1585852001.git.alexander.riesen@cetitec.com>
Signed-off-by: Alexander Riesen <alexander.riesen@cetitec.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
drivers/media/i2c/adv748x/adv748x-core.c | 24 ++++++++++++------------
drivers/media/i2c/adv748x/adv748x-csi2.c | 2 +-
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 23e02ff27b17..c3fb113cef62 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -623,11 +623,11 @@ static int adv748x_parse_dt(struct adv748x_state *state)
for_each_endpoint_of_node(state->dev->of_node, ep_np) {
of_graph_parse_endpoint(ep_np, &ep);
- adv_info(state, "Endpoint %pOF on port %d", ep.local_node,
+ adv_info(state, "Endpoint %pOF on port %d\n", ep.local_node,
ep.port);
if (ep.port >= ADV748X_PORT_MAX) {
- adv_err(state, "Invalid endpoint %pOF on port %d",
+ adv_err(state, "Invalid endpoint %pOF on port %d\n",
ep.local_node, ep.port);
continue;
@@ -635,7 +635,7 @@ static int adv748x_parse_dt(struct adv748x_state *state)
if (state->endpoints[ep.port]) {
adv_err(state,
- "Multiple port endpoints are not supported");
+ "Multiple port endpoints are not supported\n");
continue;
}
@@ -702,62 +702,62 @@ static int adv748x_probe(struct i2c_client *client)
/* Discover and process ports declared by the Device tree endpoints */
ret = adv748x_parse_dt(state);
if (ret) {
- adv_err(state, "Failed to parse device tree");
+ adv_err(state, "Failed to parse device tree\n");
goto err_free_mutex;
}
/* Configure IO Regmap region */
ret = adv748x_configure_regmap(state, ADV748X_PAGE_IO);
if (ret) {
- adv_err(state, "Error configuring IO regmap region");
+ adv_err(state, "Error configuring IO regmap region\n");
goto err_cleanup_dt;
}
ret = adv748x_identify_chip(state);
if (ret) {
- adv_err(state, "Failed to identify chip");
+ adv_err(state, "Failed to identify chip\n");
goto err_cleanup_dt;
}
/* Configure remaining pages as I2C clients with regmap access */
ret = adv748x_initialise_clients(state);
if (ret) {
- adv_err(state, "Failed to setup client regmap pages");
+ adv_err(state, "Failed to setup client regmap pages\n");
goto err_cleanup_clients;
}
/* SW reset ADV748X to its default values */
ret = adv748x_reset(state);
if (ret) {
- adv_err(state, "Failed to reset hardware");
+ adv_err(state, "Failed to reset hardware\n");
goto err_cleanup_clients;
}
/* Initialise HDMI */
ret = adv748x_hdmi_init(&state->hdmi);
if (ret) {
- adv_err(state, "Failed to probe HDMI");
+ adv_err(state, "Failed to probe HDMI\n");
goto err_cleanup_clients;
}
/* Initialise AFE */
ret = adv748x_afe_init(&state->afe);
if (ret) {
- adv_err(state, "Failed to probe AFE");
+ adv_err(state, "Failed to probe AFE\n");
goto err_cleanup_hdmi;
}
/* Initialise TXA */
ret = adv748x_csi2_init(state, &state->txa);
if (ret) {
- adv_err(state, "Failed to probe TXA");
+ adv_err(state, "Failed to probe TXA\n");
goto err_cleanup_afe;
}
/* Initialise TXB */
ret = adv748x_csi2_init(state, &state->txb);
if (ret) {
- adv_err(state, "Failed to probe TXB");
+ adv_err(state, "Failed to probe TXB\n");
goto err_cleanup_txa;
}
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index 2091cda50935..c43ce5d78723 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -72,7 +72,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd)
struct adv748x_state *state = tx->state;
int ret;
- adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB",
+ adv_dbg(state, "Registered %s (%s)\n", is_txa(tx) ? "TXA":"TXB",
sd->name);
/*
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply related
* [PATCH v5 0/9] media: adv748x: add support for HDMI audio
From: Alex Riesen @ 2020-04-02 18:33 UTC (permalink / raw)
To: Kieran Bingham
Cc: Geert Uytterhoeven, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
devel, linux-media, linux-kernel, devicetree, linux-renesas-soc
This adds minimal support for accessing the HDMI audio provided through the
I2S port available on ADV7481 and ADV7482 decoder devices by ADI.
The port carries audio signal from the decoded HDMI stream.
Currently, the driver only supports I2S in TDM, 8 channels a 24bit at 48kHz.
Furthermore, only left-justified, 8 slots, 32bit/slot TDM, at 256fs has been
ever tried.
An ADV7482 on the Renesas Salvator-X ES1.1 (R8A77950 SoC) was used during
development of this code.
Changes since v4:
- rebased on v5.6
- Add dummy ssi4 node to the rcar sound card, as the r8a77961
devices also reference salvator-common.dts.
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
Changes since v3:
- use clk_hw instead of clk
Suggested-by: Stephen Boyd <sboyd@kernel.org>
- formatting improvements and use const where possible
- removed implementation of log_status and EDID setting ioctls,
those will be submitted as separate patches.
Suggested-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Changes since v2:
- prepare/enable the clock when it is used, as it seems nothing else does
this otherwise
- give the clock a unique name to ensure it can be registered if there are
multiple adv748x devices in the system
- remove optionality note from clock cell description to ensure the device
description matches the real device (the line is always present, even
if not used)
Changes since v1:
- Add ssi4_ctrl pin group to the sound pins. The pins are responsible for
SCK4 (sample clock) WS4 and (word boundary input), and are required for
SSI audio input over I2S.
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
- Removed the audio clock C from the list of clocks of adv748x,
it is exactly the other way around.
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
- Add an instance of (currently) fixed rate I2S master clock (MCLK),
connected to the audio_clk_c line of the R-Car SoC.
Explicitly declare the device a clock producer and add it to the
list of clocks used by the audio system of the Salvator-X board.
Suggested-by: Geert Uytterhoeven <geert@linux-m68k.org>
- The implementation of DAI driver has been moved in a separate file
and modified to activate audio decoding and I2S streaming using
snd_soc_dai_... interfaces. This allows the driver to be used with
just ALSA interfaces.
- The ioctls for selecting audio output and muting have been removed,
as not applicable.
Suggested-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
I have left implementation of the QUERYCAP in, as it seems to be required
by v4l-ctl to support loading of EDID for this node. And setting the EDID
is one feature I desperately need: there are devices which plainly refuse
to talk to the sink if it does not provide EDID they like.
- A device tree configuration without audio port will disable the audio code
altogether, supporting integrations where the port is not connected.
- The patches have been re-arranged, starting with the generic changes and
changes not related to audio directly. Those will be probably sent as a
separate series later.
- The whole series has been rebased on top of v5.6-rc6
Alex Riesen (9):
media: adv748x: fix end-of-line terminators in diagnostic statements
media: adv748x: include everything adv748x.h needs into the file
media: adv748x: reduce amount of code for bitwise modifications of
device registers
media: adv748x: add definitions for audio output related registers
media: adv748x: add support for HDMI audio
media: adv748x: prepare/enable mclk when the audio is used
media: adv748x: only activate DAI if it is described in device tree
dt-bindings: adv748x: add information about serial audio interface
(I2S/TDM)
arm64: dts: renesas: salvator: add a connection from adv748x codec
(HDMI input) to the R-Car SoC
.../devicetree/bindings/media/i2c/adv748x.txt | 16 +-
.../boot/dts/renesas/r8a77950-salvator-x.dts | 3 +-
arch/arm64/boot/dts/renesas/r8a77961.dtsi | 1 +
.../boot/dts/renesas/salvator-common.dtsi | 47 ++-
drivers/media/i2c/adv748x/Makefile | 3 +-
drivers/media/i2c/adv748x/adv748x-afe.c | 6 +-
drivers/media/i2c/adv748x/adv748x-core.c | 45 +--
drivers/media/i2c/adv748x/adv748x-csi2.c | 8 +-
drivers/media/i2c/adv748x/adv748x-dai.c | 278 ++++++++++++++++++
drivers/media/i2c/adv748x/adv748x-hdmi.c | 6 +-
drivers/media/i2c/adv748x/adv748x.h | 65 +++-
11 files changed, 436 insertions(+), 42 deletions(-)
create mode 100644 drivers/media/i2c/adv748x/adv748x-dai.c
--
2.25.1.25.g9ecbe7eb18
^ permalink raw reply
* Re: [PATCH v4 4/4] phy: qcom-qmp: Use proper PWRDOWN offset for sm8150 USB
From: Wesley Cheng @ 2020-04-02 18:31 UTC (permalink / raw)
To: Manu Gautam, agross, bjorn.andersson, kishon, robh+dt,
mark.rutland, p.zabel
Cc: linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <d61723e3-17a3-366d-f476-c33931766efd@codeaurora.org>
Hi Manu,
Thanks for the feedback and review.
On 4/2/2020 12:35 AM, Manu Gautam wrote:
>
> On 3/31/2020 1:06 AM, Wesley Cheng wrote:
>> The register map for SM8150 QMP USB SSPHY has moved
>> QPHY_POWER_DOWN_CONTROL to a different offset. Allow for
>> an offset in the register table to override default value
>> if it is a DP capable PHY.
>>
>> Signed-off-by: Wesley Cheng <wcheng@codeaurora.org>
>> ---
>> drivers/phy/qualcomm/phy-qcom-qmp.c | 15 ++++++++++++---
>> 1 file changed, 12 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
>> index cc04471..4c0517e 100644
>> --- a/drivers/phy/qualcomm/phy-qcom-qmp.c
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c
>> @@ -164,6 +164,7 @@ enum qphy_reg_layout {
>> [QPHY_SW_RESET] = 0x00,
>> [QPHY_START_CTRL] = 0x44,
>> [QPHY_PCS_STATUS] = 0x14,
>> + [QPHY_COM_POWER_DOWN_CONTROL] = 0x40,
> Since this is in PCS block please rename it to -
>
> QPHY_PCS_POWER_DOWN_CONTROL
>
Sure, will add another enum value to the register layout, and rename it
where needed.
>> };
>>
>> static const unsigned int sdm845_ufsphy_regs_layout[] = {
>> @@ -1627,6 +1628,9 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
>> if (cfg->has_phy_com_ctrl)
>> qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
>> SW_PWRDN);
>> + else if (cfg->has_phy_dp_com_ctrl && cfg->regs[QPHY_COM_POWER_DOWN_CONTROL])
>> + qphy_setbits(pcs, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
>> + cfg->pwrdn_ctrl);
>> else
>> qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
> Since, this register is in PCS block why check for dp_com_ctrl here?
> Something like:
>
> if (cfg->has_phy_com_ctrl) {
> qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
> SW_PWRDN);
> } else {
> if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
> qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
> cfg->pwrdn_ctrl);
> else
> qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
> }
>
Agree with this.
>>
>> @@ -1671,10 +1675,12 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
>> return ret;
>> }
>>
>> -static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
>> +static int qcom_qmp_phy_com_exit(struct qmp_phy *qphy)
>> {
>> + struct qcom_qmp *qmp = qphy->qmp;
>> const struct qmp_phy_cfg *cfg = qmp->cfg;
>> void __iomem *serdes = qmp->serdes;
>> + void __iomem *pcs = qphy->pcs;
>> int i = cfg->num_resets;
>>
>> mutex_lock(&qmp->phy_mutex);
>> @@ -1691,6 +1697,9 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
>> SW_RESET);
>> qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
>> SW_PWRDN);
>> + } else if (cfg->has_phy_dp_com_ctrl && cfg->regs[QPHY_COM_POWER_DOWN_CONTROL]) {
>
> Can we add change similar to init() here ?
>
>
Sure. I will move this check to where the current code writes to the
PWR DOWN CONTROL in
static int qcom_qmp_phy_disable(struct phy *phy)
{
...
qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
We wouldn't want the SW to write to an incorrect register.
>> + cfg->pwrdn_ctrl);
>> }
>>
>> while (--i >= 0)
>> @@ -1829,7 +1838,7 @@ static int qcom_qmp_phy_enable(struct phy *phy)
>> if (cfg->has_lane_rst)
>> reset_control_assert(qphy->lane_rst);
>> err_lane_rst:
>> - qcom_qmp_phy_com_exit(qmp);
>> + qcom_qmp_phy_com_exit(qphy);
>>
>> return ret;
>> }
>> @@ -1855,7 +1864,7 @@ static int qcom_qmp_phy_disable(struct phy *phy)
>> if (cfg->has_lane_rst)
>> reset_control_assert(qphy->lane_rst);
>>
>> - qcom_qmp_phy_com_exit(qmp);
>> + qcom_qmp_phy_com_exit(qphy);
>>
>> qmp->phy_initialized = false;
>>
>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* Re: [PATCH net-next v6 00/11] net: ethernet: ti: add networking support for k3 am65x/j721e soc
From: Nick Desaulniers @ 2020-04-02 17:27 UTC (permalink / raw)
To: Grygorii Strashko
Cc: Will Deacon, David S. Miller, Arnd Bergmann, devicetree, kishon,
Jakub Kicinski, Linux ARM, LKML, m-karicheri2,
Network Development, nsekhar, Olof Johansson, olteanv,
peter.ujfalusi, Rob Herring, rogerq, t-kristo, clang-built-linux
In-Reply-To: <adc2aa08-60e2-cdc3-6b5b-6d96f8805c44@ti.com>
On Thu, Apr 2, 2020 at 4:05 AM Grygorii Strashko
<grygorii.strashko@ti.com> wrote:
>
>
>
> On 02/04/2020 12:42, Will Deacon wrote:
> > On Wed, Apr 01, 2020 at 03:35:00PM -0700, Nick Desaulniers wrote:
> >>>> I think the ARM64 build is now also broken on Linus' master branch,
> >>>> after the net-next merge? I am not quite sure if the device tree
> >>>> patches were supposed to land in mainline the way they did.
> >>>
> >>> There's a fix in my net tree and it will go to Linus soon.
> >>>
> >>> There is no clear policy for dt change integration, and honestly
> >>> I try to deal with the situation on a case by case basis.
> >>
> >> Yep, mainline aarch64-linux-gnu- builds are totally hosed. DTC fails the build
> >> very early on:
> >> https://travis-ci.com/github/ClangBuiltLinux/continuous-integration/jobs/311246218
> >> https://travis-ci.com/github/ClangBuiltLinux/continuous-integration/jobs/311246270
> >> There was no failure in -next, not sure how we skipped our canary in the coal
> >> mine.
> >
> > Yes, one of the things linux-next does a really good job at catching is
> > build breakage so it would've been nice to have seen this there rather
> > than end up with breakage in Linus' tree :(
> >
> > Was the timing just bad, or are we missing DT coverage or something else?
>
> It seems issue was not caught in -next because the patch that fixes the issue was already in -next
> before this series was pushed.
>
> Sorry for the mess again.
No worries, it's just worthwhile to study failures. So IIUC, in this case:
mainline was 5.6
the broken patch was merged in 5.7 merge window
a fix was already in -next, but not slated for the new merge window.
(Maybe scheduled for 5.8?)
So it sounds like it can be dangerous to have 2 branches from 1 tree
flow into -next, as the branch meant for a later release can mask
failures in pull requests for the earlier release?
Do we know what and where the fix currently is?
Can we make sure it's sent to Linus for 5.7-rc1? (Or sooner?)
--
Thanks,
~Nick Desaulniers
^ permalink raw reply
* Re: [PATCH 4/4] arm64: dts: rockchip: add isp0 node for rk3399
From: Johan Jonker @ 2020-04-02 17:20 UTC (permalink / raw)
To: helen.koike
Cc: dafna.hirschfeld, devel, devicetree, ezequiel, heiko,
hverkuil-cisco, karthik.poduval, kernel, linux-kernel,
linux-media, linux-rockchip, mark.rutland, robh+dt
In-Reply-To: <20200402000234.226466-5-helen.koike@collabora.com>
Hi Helen,
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index fc0295d2a65a1..815099a0cd0dd 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -1718,6 +1718,33 @@ vopb_mmu: iommu@ff903f00 {
> status = "disabled";
> };
>
> + isp0: isp0@ff910000 {
> + compatible = "rockchip,rk3399-cif-isp";
> + reg = <0x0 0xff910000 0x0 0x4000>;
> + interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>;
> + clocks = <&cru SCLK_ISP0>,
> + <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>,
> + <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>;
> + clock-names = "clk_isp",
> + "aclk_isp", "aclk_isp_wrap",
> + "hclk_isp", "hclk_isp_wrap";
> + power-domains = <&power RK3399_PD_ISP0>;
> + iommus = <&isp0_mmu>;
> + phys = <&mipi_dphy_rx0>;
> + phy-names = "dphy";
Maybe a little sort? But keep rest as it is. Also in example.
iommus = <&isp0_mmu>;
phys = <&mipi_dphy_rx0>;
phy-names = "dphy";
power-domains = <&power RK3399_PD_ISP0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + reg = <0>;
Move reg above #address-cells. Change that in example as well.
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
> + };
> + };
> + };
> +
> isp0_mmu: iommu@ff914000 {
> compatible = "rockchip,iommu";
> reg = <0x0 0xff914000 0x0 0x100>, <0x0 0xff915000 0x0 0x100>;
> --
> 2.26.0
^ permalink raw reply
* Re: [v6, 5/5] media: platform: Add Mediatek ISP P1 V4L2 device driver
From: Dafna Hirschfeld @ 2020-04-02 16:45 UTC (permalink / raw)
To: Jungo Lin, tfiga, hverkuil-cisco, laurent.pinchart, matthias.bgg,
mchehab
Cc: linux-media, linux-mediatek, linux-arm-kernel, devicetree,
srv_heupstream, ddavenport, robh, Sean.Cheng, sj.huang,
frederic.chen, Jerry-ch.Chen, frankie.chiu, ryan.yu, Rynn.Wu,
yuzhao, zwisler, shik, suleiman, Pi-Hsun Shih
In-Reply-To: <20191219054930.29513-6-jungo.lin@mediatek.com>
On 19.12.19 06:49, Jungo Lin wrote:
> This patch adds the Mediatek ISP P1 HW control device driver.
> It handles the ISP HW configuration, provides interrupt handling and
> initializes the V4L2 device nodes and other V4L2 functions. Moreover,
> implement standard V4L2 video driver that utilizes V4L2 and media
> framework APIs. It supports one media device, one sub-device and
> several video devices during initialization. Moreover, it also connects
> with sensor and seninf drivers with V4L2 async APIs. Communicate with
> co-process via SCP communication to compose ISP registers in the
> firmware.
>
> (The current metadata interface used in meta input and partial
> meta nodes is only a temporary solution to kick off the driver
> development and is not ready to be reviewed yet.)
>
> Signed-off-by: Jungo Lin <jungo.lin@mediatek.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> Signed-off-by: Pi-Hsun Shih <pihsun@chromium.org>
> ---
> Changes from v6:
> - Revise help description for VIDEO_MEDIATEK_ISP_PASS1
> - Apply SCP v21 change in P1 driver by Pi-Hsun Shih
> - Correct auto suspend timer value for suspend/resume issue
> - Increase IPI guard timer to 1 second to avoid false alarm command timeout event
> - Fix KE due to no sen-inf sub-device
> ---
> drivers/media/platform/mtk-isp/Kconfig | 20 +
> .../media/platform/mtk-isp/isp_50/Makefile | 3 +
> .../platform/mtk-isp/isp_50/cam/Makefile | 6 +
> .../platform/mtk-isp/isp_50/cam/mtk_cam-hw.c | 636 +++++
> .../platform/mtk-isp/isp_50/cam/mtk_cam-hw.h | 64 +
> .../platform/mtk-isp/isp_50/cam/mtk_cam-ipi.h | 222 ++
> .../mtk-isp/isp_50/cam/mtk_cam-regs.h | 95 +
> .../platform/mtk-isp/isp_50/cam/mtk_cam.c | 2087 +++++++++++++++++
> .../platform/mtk-isp/isp_50/cam/mtk_cam.h | 244 ++
> 9 files changed, 3377 insertions(+)
> create mode 100644 drivers/media/platform/mtk-isp/Kconfig
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/Makefile
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.c
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.h
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ipi.h
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> create mode 100644 drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
>
> diff --git a/drivers/media/platform/mtk-isp/Kconfig b/drivers/media/platform/mtk-isp/Kconfig
> new file mode 100644
> index 000000000000..f86e1b59ad1e
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/Kconfig
> @@ -0,0 +1,20 @@
> +config VIDEO_MEDIATEK_ISP_PASS1
> + tristate "Mediatek ISP Pass 1 driver"
> + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> + depends on ARCH_MEDIATEK
> + select V4L2_FWNODE
> + select VIDEOBUF2_VMALLOC
> + select VIDEOBUF2_DMA_CONTIG
> + select MTK_SCP
> + default n
> + help
> + Pass 1 driver controls 3A (auto-focus, exposure,
> + and white balance) with tuning feature and outputs
> + the captured image buffers in Mediatek's camera system.
> +
> + Choose Y if you want to use Mediatek SoCs to create image
> + captured application such as video recording and still image
> + capturing.
> +
> + To compile this driver as a module, choose M here; the module
> + will be called mtk-cam-isp.
> diff --git a/drivers/media/platform/mtk-isp/isp_50/Makefile b/drivers/media/platform/mtk-isp/isp_50/Makefile
> new file mode 100644
> index 000000000000..ce79d283b209
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += cam/
> \ No newline at end of file
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/Makefile b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> new file mode 100644
> index 000000000000..53b54d3c26a0
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +mtk-cam-isp-objs += mtk_cam.o
> +mtk-cam-isp-objs += mtk_cam-hw.o
> +
> +obj-$(CONFIG_VIDEO_MEDIATEK_ISP_PASS1) += mtk-cam-isp.o
> \ No newline at end of file
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.c
> new file mode 100644
> index 000000000000..4065d0d29b7f
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.c
> @@ -0,0 +1,636 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2019 MediaTek Inc.
> +
> +#include <linux/atomic.h>
> +#include <linux/clk.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/module.h>
> +#include <linux/remoteproc/mtk_scp.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/remoteproc.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +#include <linux/vmalloc.h>
> +
> +#include <media/v4l2-event.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-hw.h"
> +#include "mtk_cam-regs.h"
> +
> +#define MTK_ISP_COMPOSER_MEM_SIZE 0x200000
> +#define MTK_ISP_CQ_BUFFER_COUNT 3
> +#define MTK_ISP_CQ_ADDRESS_OFFSET 0x640
> +
> +/*
> + *
> + * MTK Camera ISP P1 HW supports 3 ISP HW (CAM A/B/C).
> + * The T-put capability of CAM B is the maximum (max line buffer: 5376 pixels)
> + * For CAM A/C, it only supports max line buffer with 3328 pixels.
> + * In current driver, only supports CAM B.
> + *
> + */
> +#define MTK_ISP_CAM_ID_B 3
> +#define MTK_ISP_AUTOSUSPEND_DELAY_MS 66
> +#define MTK_ISP_IPI_SEND_TIMEOUT 1000
> +#define MTK_ISP_STOP_HW_TIMEOUT (33 * USEC_PER_MSEC)
> +
> +static void isp_tx_frame_worker(struct work_struct *work)
> +{
> + struct mtk_cam_dev_request *req =
> + container_of(work, struct mtk_cam_dev_request, frame_work);
> + struct mtk_cam_dev *cam =
> + container_of(req->req.mdev, struct mtk_cam_dev, media_dev);
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(cam->dev);
> +
> + scp_ipi_send(p1_dev->scp, SCP_IPI_ISP_FRAME, &req->frame_params,
> + sizeof(req->frame_params), MTK_ISP_IPI_SEND_TIMEOUT);
> +}
> +
> +static void isp_composer_handler(void *data, unsigned int len, void *priv)
> +{
> + struct mtk_isp_p1_device *p1_dev = (struct mtk_isp_p1_device *)priv;
> + struct device *dev = p1_dev->dev;
> + struct mtk_isp_scp_p1_cmd *ipi_msg;
> +
> + ipi_msg = (struct mtk_isp_scp_p1_cmd *)data;
> +
> + if (len < offsetofend(struct mtk_isp_scp_p1_cmd, ack_info)) {
> + dev_err(dev, "wrong IPI len:%d\n", len);
> + return;
> + }
> +
> + if (ipi_msg->cmd_id != ISP_CMD_ACK ||
> + ipi_msg->ack_info.cmd_id != ISP_CMD_FRAME_ACK)
> + return;
> +
> + p1_dev->composed_frame_seq_no = ipi_msg->ack_info.frame_seq_no;
> + dev_dbg(dev, "ack frame_num:%d\n", p1_dev->composed_frame_seq_no);
> +}
> +
> +static int isp_composer_init(struct mtk_isp_p1_device *p1_dev)
> +{
> + struct device *dev = p1_dev->dev;
> + int ret;
> +
> + ret = scp_ipi_register(p1_dev->scp, SCP_IPI_ISP_CMD,
> + isp_composer_handler, p1_dev);
> + if (ret) {
> + dev_err(dev, "failed to register IPI cmd\n");
> + return ret;
> + }
> + ret = scp_ipi_register(p1_dev->scp, SCP_IPI_ISP_FRAME,
> + isp_composer_handler, p1_dev);
> + if (ret) {
> + dev_err(dev, "failed to register IPI frame\n");
> + goto unreg_ipi_cmd;
> + }
> +
> + p1_dev->composer_wq =
> + alloc_ordered_workqueue(dev_name(p1_dev->dev),
> + __WQ_LEGACY | WQ_MEM_RECLAIM |
> + WQ_FREEZABLE);
> + if (!p1_dev->composer_wq) {
> + dev_err(dev, "failed to alloc composer workqueue\n");
> + goto unreg_ipi_frame;
> + }
> +
> + return 0;
> +
> +unreg_ipi_frame:
> + scp_ipi_unregister(p1_dev->scp, SCP_IPI_ISP_FRAME);
> +unreg_ipi_cmd:
> + scp_ipi_unregister(p1_dev->scp, SCP_IPI_ISP_CMD);
> +
> + return ret;
> +}
> +
> +static void isp_composer_uninit(struct mtk_isp_p1_device *p1_dev)
> +{
> + destroy_workqueue(p1_dev->composer_wq);
> + scp_ipi_unregister(p1_dev->scp, SCP_IPI_ISP_CMD);
> + scp_ipi_unregister(p1_dev->scp, SCP_IPI_ISP_FRAME);
> +}
> +
> +static void isp_composer_hw_init(struct mtk_isp_p1_device *p1_dev)
> +{
> + struct mtk_isp_scp_p1_cmd composer_tx_cmd;
> +
> + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
> + composer_tx_cmd.cmd_id = ISP_CMD_INIT;
> + composer_tx_cmd.init_param.hw_module = MTK_ISP_CAM_ID_B;
> +
> + /*
> + * Passed coherent reserved memory info. for SCP firmware usage.
> + * This buffer is used for SCP's ISP composer to compose.
> + * The size of is fixed to 0x200000 for the requirement of composer.
> + */
> + composer_tx_cmd.init_param.cq_addr.iova = p1_dev->composer_iova;
> + composer_tx_cmd.init_param.cq_addr.scp_addr = p1_dev->composer_scp_addr;
> +
> + scp_ipi_send(p1_dev->scp, SCP_IPI_ISP_CMD, &composer_tx_cmd,
> + sizeof(composer_tx_cmd), MTK_ISP_IPI_SEND_TIMEOUT);
> +}
> +
> +static void isp_composer_hw_deinit(struct mtk_isp_p1_device *p1_dev)
> +{
> + struct mtk_isp_scp_p1_cmd composer_tx_cmd;
> +
> + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
> + composer_tx_cmd.cmd_id = ISP_CMD_DEINIT;
> +
> + scp_ipi_send(p1_dev->scp, SCP_IPI_ISP_CMD, &composer_tx_cmd,
> + sizeof(composer_tx_cmd), MTK_ISP_IPI_SEND_TIMEOUT);
> +
> + isp_composer_uninit(p1_dev);
> +}
> +
> +void mtk_isp_hw_config(struct mtk_cam_dev *cam,
> + struct p1_config_param *config_param)
> +{
> + struct mtk_isp_scp_p1_cmd composer_tx_cmd;
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(cam->dev);
> +
> + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
> + composer_tx_cmd.cmd_id = ISP_CMD_CONFIG;
> + memcpy(&composer_tx_cmd.config_param, config_param,
> + sizeof(*config_param));
> +
> + scp_ipi_send(p1_dev->scp, SCP_IPI_ISP_CMD, &composer_tx_cmd,
> + sizeof(composer_tx_cmd), MTK_ISP_IPI_SEND_TIMEOUT);
> +}
> +
> +void mtk_isp_stream(struct mtk_cam_dev *cam, int on)
> +{
> + struct mtk_isp_scp_p1_cmd composer_tx_cmd;
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(cam->dev);
> +
> + memset(&composer_tx_cmd, 0, sizeof(composer_tx_cmd));
> + composer_tx_cmd.cmd_id = ISP_CMD_STREAM;
> + composer_tx_cmd.is_stream_on = on;
> +
> + scp_ipi_send(p1_dev->scp, SCP_IPI_ISP_CMD, &composer_tx_cmd,
> + sizeof(composer_tx_cmd), MTK_ISP_IPI_SEND_TIMEOUT);
> +}
> +
> +int mtk_isp_hw_init(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> + int ret;
> +
> + ret = rproc_boot(p1_dev->rproc_handle);
> + if (ret) {
> + dev_err(dev, "failed to rproc_boot\n");
> + return ret;
> + }
> +
> + ret = isp_composer_init(p1_dev);
> + if (ret)
> + return ret;
> +
> + pm_runtime_get_sync(dev);
> + isp_composer_hw_init(p1_dev);
> +
> + p1_dev->enqueued_frame_seq_no = 0;
> + p1_dev->dequeued_frame_seq_no = 0;
> + p1_dev->composed_frame_seq_no = 0;
> + p1_dev->sof_count = 0;
> +
> + dev_dbg(dev, "%s done\n", __func__);
> +
> + return 0;
> +}
> +
> +int mtk_isp_hw_release(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> +
> + isp_composer_hw_deinit(p1_dev);
> + pm_runtime_mark_last_busy(dev);
> + pm_runtime_put_autosuspend(dev);
> + rproc_shutdown(p1_dev->rproc_handle);
> +
> + dev_dbg(dev, "%s done\n", __func__);
> +
> + return 0;
> +}
> +
> +void mtk_isp_req_enqueue(struct mtk_cam_dev *cam,
> + struct mtk_cam_dev_request *req)
> +{
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(cam->dev);
> +
> + /* Accumulated frame sequence number */
> + req->frame_params.frame_seq_no = ++p1_dev->enqueued_frame_seq_no;
> +
> + INIT_WORK(&req->frame_work, isp_tx_frame_worker);
> + queue_work(p1_dev->composer_wq, &req->frame_work);
> + dev_dbg(cam->dev, "enqueue fd:%s frame_seq_no:%d job cnt:%d\n",
> + req->req.debug_str, req->frame_params.frame_seq_no,
> + cam->running_job_count);
> +}
> +
> +static void isp_irq_handle_sof(struct mtk_isp_p1_device *p1_dev,
> + unsigned int dequeued_frame_seq_no)
> +{
> + dma_addr_t base_addr = p1_dev->composer_iova;
> + struct device *dev = p1_dev->dev;
> + struct mtk_cam_dev_request *req;
> + int composed_frame_seq_no = p1_dev->composed_frame_seq_no;
> + unsigned int addr_offset;
> +
> + /* Send V4L2_EVENT_FRAME_SYNC event */
> + mtk_cam_dev_event_frame_sync(&p1_dev->cam_dev, dequeued_frame_seq_no);
> +
> + p1_dev->sof_count += 1;
> + /* Save frame information */
> + p1_dev->dequeued_frame_seq_no = dequeued_frame_seq_no;
> +
> + req = mtk_cam_dev_get_req(&p1_dev->cam_dev, dequeued_frame_seq_no);
> + if (req)
> + req->timestamp = ktime_get_boottime_ns();
> +
> + /* Update CQ base address if needed */
> + if (composed_frame_seq_no <= dequeued_frame_seq_no) {
> + dev_dbg(dev,
> + "SOF_INT_ST, no update, cq_num:%d, frame_seq:%d\n",
> + composed_frame_seq_no, dequeued_frame_seq_no);
> + return;
> + }
> + addr_offset = MTK_ISP_CQ_ADDRESS_OFFSET *
> + (dequeued_frame_seq_no % MTK_ISP_CQ_BUFFER_COUNT);
> + writel(base_addr + addr_offset, p1_dev->regs + REG_CQ_THR0_BASEADDR);
> + dev_dbg(dev,
> + "SOF_INT_ST, update next, cq_num:%d, frame_seq:%d cq_addr:0x%x\n",
> + composed_frame_seq_no, dequeued_frame_seq_no, addr_offset);
> +}
> +
> +static void isp_irq_handle_dma_err(struct mtk_isp_p1_device *p1_dev)
> +{
> + u32 val;
> +
> + dev_err(p1_dev->dev,
> + "IMGO:0x%x, RRZO:0x%x, AAO=0x%x, AFO=0x%x, LMVO=0x%x\n",
> + readl(p1_dev->regs + REG_IMGO_ERR_STAT),
> + readl(p1_dev->regs + REG_RRZO_ERR_STAT),
> + readl(p1_dev->regs + REG_AAO_ERR_STAT),
> + readl(p1_dev->regs + REG_AFO_ERR_STAT),
> + readl(p1_dev->regs + REG_LMVO_ERR_STAT));
> + dev_err(p1_dev->dev,
> + "LCSO=0x%x, PSO=0x%x, FLKO=0x%x, BPCI:0x%x, LSCI=0x%x\n",
> + readl(p1_dev->regs + REG_LCSO_ERR_STAT),
> + readl(p1_dev->regs + REG_PSO_ERR_STAT),
> + readl(p1_dev->regs + REG_FLKO_ERR_STAT),
> + readl(p1_dev->regs + REG_BPCI_ERR_STAT),
> + readl(p1_dev->regs + REG_LSCI_ERR_STAT));
> +
> + /* Disable DMA error mask to avoid too much error log */
> + val = readl(p1_dev->regs + REG_CTL_RAW_INT_EN);
> + writel((val & (~DMA_ERR_INT_EN)), p1_dev->regs + REG_CTL_RAW_INT_EN);
> + dev_dbg(p1_dev->dev, "disable DMA error mask:0x%x\n", val);
> +}
> +
> +static irqreturn_t isp_irq_cam(int irq, void *data)
> +{
> + struct mtk_isp_p1_device *p1_dev = (struct mtk_isp_p1_device *)data;
> + struct device *dev = p1_dev->dev;
> + unsigned int dequeued_frame_seq_no;
> + unsigned int irq_status, err_status, dma_status;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&p1_dev->spinlock_irq, flags);
> + irq_status = readl(p1_dev->regs + REG_CTL_RAW_INT_STAT);
> + err_status = irq_status & INT_ST_MASK_CAM_ERR;
> + dma_status = readl(p1_dev->regs + REG_CTL_RAW_INT2_STAT);
> + dequeued_frame_seq_no = readl(p1_dev->regs + REG_FRAME_SEQ_NUM);
> + spin_unlock_irqrestore(&p1_dev->spinlock_irq, flags);
> +
> + /*
> + * In normal case, the next SOF ISR should come after HW PASS1 DONE ISR.
> + * If these two ISRs come together, print warning msg to hint.
> + */
> + if ((irq_status & SOF_INT_ST) && (irq_status & HW_PASS1_DON_ST))
> + dev_dbg(dev, "sof_done block cnt:%d\n", p1_dev->sof_count);
> +
> + /* De-queue frame */
> + if (irq_status & SW_PASS1_DON_ST) {
> + mtk_cam_dev_dequeue_req_frame(&p1_dev->cam_dev,
> + p1_dev->dequeued_frame_seq_no);
> + mtk_cam_dev_req_try_queue(&p1_dev->cam_dev);
> + }
> +
> + /* Save frame info. & update CQ address for frame HW en-queue */
> + if (irq_status & SOF_INT_ST)
> + isp_irq_handle_sof(p1_dev, dequeued_frame_seq_no);
> +
> + /* Check ISP error status */
> + if (err_status) {
> + dev_err(dev, "int_err:0x%x 0x%x\n", irq_status, err_status);
> + /* Show DMA errors in detail */
> + if (err_status & DMA_ERR_ST)
> + isp_irq_handle_dma_err(p1_dev);
> + }
> +
> + dev_dbg(dev, "SOF:%d irq:0x%x, dma:0x%x, frame_num:%d\n",
> + p1_dev->sof_count, irq_status, dma_status,
> + dequeued_frame_seq_no);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int isp_setup_scp_rproc(struct mtk_isp_p1_device *p1_dev,
> + struct platform_device *pdev)
> +{
> + struct device *dev = p1_dev->dev;
> + dma_addr_t addr;
> + void *ptr;
> + int ret;
> +
> + p1_dev->scp = scp_get(pdev);
> + if (!p1_dev->scp) {
> + dev_err(dev, "failed to get scp device\n");
> + return -ENODEV;
> + }
> +
> + p1_dev->rproc_handle = scp_get_rproc(p1_dev->scp);
> + dev_dbg(dev, "p1 rproc_phandle: 0x%pK\n", p1_dev->rproc_handle);
> + p1_dev->cam_dev.smem_dev = scp_get_device(p1_dev->scp);
> +
> + /*
> + * Allocate coherent reserved memory for SCP firmware usage.
> + * The size of SCP composer's memory is fixed to 0x200000
> + * for the requirement of firmware.
> + */
> + ptr = dma_alloc_coherent(p1_dev->cam_dev.smem_dev,
> + MTK_ISP_COMPOSER_MEM_SIZE, &addr, GFP_KERNEL);
> + if (!ptr) {
> + ret = -ENOMEM;
> + goto fail_put_scp;
> + }
> +
> + p1_dev->composer_scp_addr = addr;
> + p1_dev->composer_virt_addr = ptr;
> + dev_dbg(dev, "scp addr:%pad va:%pK\n", &addr, ptr);
> +
> + /*
> + * This reserved memory is also be used by ISP P1 HW.
> + * Need to get iova address for ISP P1 DMA.
> + */
> + addr = dma_map_resource(dev, addr, MTK_ISP_COMPOSER_MEM_SIZE,
> + DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
> + if (dma_mapping_error(dev, addr)) {
> + dev_err(dev, "failed to map scp iova\n");
> + ret = -ENOMEM;
> + goto fail_free_mem;
> + }
> + p1_dev->composer_iova = addr;
> + dev_dbg(dev, "scp iova addr:%pad\n", &addr);
> +
> + return 0;
> +
> +fail_free_mem:
> + dma_free_coherent(p1_dev->cam_dev.smem_dev, MTK_ISP_COMPOSER_MEM_SIZE,
> + p1_dev->composer_virt_addr,
> + p1_dev->composer_scp_addr);
> + p1_dev->composer_scp_addr = 0;
> +fail_put_scp:
> + scp_put(p1_dev->scp);
> +
> + return ret;
> +}
> +
> +static void isp_teardown_scp_rproc(struct mtk_isp_p1_device *p1_dev)
> +{
> + dma_free_coherent(p1_dev->cam_dev.smem_dev, MTK_ISP_COMPOSER_MEM_SIZE,
> + p1_dev->composer_virt_addr,
> + p1_dev->composer_scp_addr);
> + p1_dev->composer_scp_addr = 0;
> + scp_put(p1_dev->scp);
> +}
> +
> +static int mtk_isp_pm_suspend(struct device *dev)
> +{
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> + u32 val;
> + int ret;
> +
> + dev_dbg(dev, "- %s\n", __func__);
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
> +
> + /* Disable ISP's view finder and wait for TG idle if possible */
> + dev_dbg(dev, "cam suspend, disable VF\n");
> + val = readl(p1_dev->regs + REG_TG_VF_CON);
> + writel(val & (~TG_VF_CON_VFDATA_EN), p1_dev->regs + REG_TG_VF_CON);
> + readl_poll_timeout_atomic(p1_dev->regs + REG_TG_INTER_ST, val,
> + (val & TG_CS_MASK) == TG_IDLE_ST,
> + USEC_PER_MSEC, MTK_ISP_STOP_HW_TIMEOUT);
> +
> + /* Disable CMOS */
> + val = readl(p1_dev->regs + REG_TG_SEN_MODE);
> + writel(val & (~TG_SEN_MODE_CMOS_EN), p1_dev->regs + REG_TG_SEN_MODE);
> +
> + /* Force ISP HW to idle */
> + ret = pm_runtime_force_suspend(dev);
> + if (ret) {
> + dev_err(dev, "failed to force suspend:%d\n", ret);
> + goto reenable_hw;
> + }
> +
> + return 0;
> +
> +reenable_hw:
> + val = readl(p1_dev->regs + REG_TG_SEN_MODE);
> + writel(val | TG_SEN_MODE_CMOS_EN, p1_dev->regs + REG_TG_SEN_MODE);
> + val = readl(p1_dev->regs + REG_TG_VF_CON);
> + writel(val | TG_VF_CON_VFDATA_EN, p1_dev->regs + REG_TG_VF_CON);
> +
> + return ret;
> +}
> +
> +static int mtk_isp_pm_resume(struct device *dev)
> +{
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> + u32 val;
> + int ret;
> +
> + dev_dbg(dev, "- %s\n", __func__);
> +
> + if (pm_runtime_suspended(dev))
> + return 0;
> +
> + /* Force ISP HW to resume */
> + ret = pm_runtime_force_resume(dev);
> + if (ret)
> + return ret;
> +
> + /* Enable CMOS */
> + dev_dbg(dev, "cam resume, enable CMOS/VF\n");
> + val = readl(p1_dev->regs + REG_TG_SEN_MODE);
> + writel(val | TG_SEN_MODE_CMOS_EN, p1_dev->regs + REG_TG_SEN_MODE);
> +
> + /* Enable VF */
> + val = readl(p1_dev->regs + REG_TG_VF_CON);
> + writel(val | TG_VF_CON_VFDATA_EN, p1_dev->regs + REG_TG_VF_CON);
> +
> + return 0;
> +}
> +
> +static int mtk_isp_runtime_suspend(struct device *dev)
> +{
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> +
> + dev_dbg(dev, "%s:disable clock\n", __func__);
> + clk_bulk_disable_unprepare(p1_dev->num_clks, p1_dev->clks);
> +
> + return 0;
> +}
> +
> +static int mtk_isp_runtime_resume(struct device *dev)
> +{
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> + int ret;
> +
> + dev_dbg(dev, "%s:enable clock\n", __func__);
> + ret = clk_bulk_prepare_enable(p1_dev->num_clks, p1_dev->clks);
> + if (ret) {
> + dev_err(dev, "failed to enable clock:%d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_isp_probe(struct platform_device *pdev)
> +{
> + /* List of clocks required by isp cam */
> + static const char * const clk_names[] = {
> + "camsys_cam_cgpdn", "camsys_camtg_cgpdn"
> + };
> + struct mtk_isp_p1_device *p1_dev;
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + int irq, ret, i;
> +
> + p1_dev = devm_kzalloc(dev, sizeof(*p1_dev), GFP_KERNEL);
> + if (!p1_dev)
> + return -ENOMEM;
> +
> + p1_dev->dev = dev;
> + dev_set_drvdata(dev, p1_dev);
> +
> + /*
> + * Now only support single CAM with CAM B.
> + * Get CAM B register base with CAM B index.
> + * Support multiple CAMs in future.
> + */
> + res = platform_get_resource(pdev, IORESOURCE_MEM, MTK_ISP_CAM_ID_B);
> + p1_dev->regs = devm_ioremap_resource(dev, res);
> + if (IS_ERR(p1_dev->regs)) {
> + dev_err(dev, "failed to map reister base\n");
> + return PTR_ERR(p1_dev->regs);
> + }
> + dev_dbg(dev, "cam, map_addr=0x%pK\n", p1_dev->regs);
> +
> + /*
> + * The cam_sys unit only supports reg., but has no IRQ support.
> + * The reg. & IRQ index is shifted with 1 for CAM B in DTS.
> + */
> + irq = platform_get_irq(pdev, MTK_ISP_CAM_ID_B - 1);
> + if (!irq) {
> + dev_err(dev, "failed to get irq\n");
> + return -ENODEV;
> + }
> + ret = devm_request_irq(dev, irq, isp_irq_cam, 0, dev_name(dev),
> + p1_dev);
> + if (ret) {
> + dev_err(dev, "failed to request irq=%d\n", irq);
> + return ret;
> + }
> + dev_dbg(dev, "registered irq=%d\n", irq);
> + spin_lock_init(&p1_dev->spinlock_irq);
> +
> + p1_dev->num_clks = ARRAY_SIZE(clk_names);
> + p1_dev->clks = devm_kcalloc(dev, p1_dev->num_clks,
> + sizeof(*p1_dev->clks), GFP_KERNEL);
> + if (!p1_dev->clks)
> + return -ENOMEM;
> +
> + for (i = 0; i < p1_dev->num_clks; ++i)
> + p1_dev->clks[i].id = clk_names[i];
> +
> + ret = devm_clk_bulk_get(dev, p1_dev->num_clks, p1_dev->clks);
> + if (ret) {
> + dev_err(dev, "failed to get isp cam clock:%d\n", ret);
> + return ret;
> + }
> +
> + ret = isp_setup_scp_rproc(p1_dev, pdev);
> + if (ret)
> + return ret;
> +
> + pm_runtime_set_autosuspend_delay(dev, MTK_ISP_AUTOSUSPEND_DELAY_MS);
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_enable(dev);
> +
> + /* Initialize the v4l2 common part */
> + ret = mtk_cam_dev_init(pdev, &p1_dev->cam_dev);
> + if (ret) {
> + isp_teardown_scp_rproc(p1_dev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mtk_isp_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mtk_isp_p1_device *p1_dev = dev_get_drvdata(dev);
> +
> + mtk_cam_dev_cleanup(&p1_dev->cam_dev);
> + pm_runtime_dont_use_autosuspend(dev);
> + pm_runtime_disable(dev);
> + dma_unmap_page_attrs(dev, p1_dev->composer_iova,
> + MTK_ISP_COMPOSER_MEM_SIZE, DMA_TO_DEVICE,
> + DMA_ATTR_SKIP_CPU_SYNC);
> + isp_teardown_scp_rproc(p1_dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops mtk_isp_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(mtk_isp_pm_suspend, mtk_isp_pm_resume)
> + SET_RUNTIME_PM_OPS(mtk_isp_runtime_suspend, mtk_isp_runtime_resume,
> + NULL)
> +};
> +
> +static const struct of_device_id mtk_isp_of_ids[] = {
> + {.compatible = "mediatek,mt8183-camisp",},
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_isp_of_ids);
> +
> +static struct platform_driver mtk_isp_driver = {
> + .probe = mtk_isp_probe,
> + .remove = mtk_isp_remove,
> + .driver = {
> + .name = "mtk-cam-p1",
> + .of_match_table = of_match_ptr(mtk_isp_of_ids),
> + .pm = &mtk_isp_pm_ops,
> + }
> +};
> +
> +module_platform_driver(mtk_isp_driver);
> +
> +MODULE_DESCRIPTION("Mediatek ISP P1 driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.h
> new file mode 100644
> index 000000000000..837662f92a5e
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-hw.h
> @@ -0,0 +1,64 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_CAM_HW_H__
> +#define __MTK_CAM_HW_H__
> +
> +#include <linux/types.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-ipi.h"
> +
> +/*
> + * struct mtk_isp_p1_device - the Mediatek ISP P1 device information
> + *
> + * @dev: Pointer to device.
> + * @scp_pdev: Pointer to SCP platform device.
> + * @rproc_handle: Pointer to new remoteproc instance.
> + * @cam_dev: Embedded struct cam_dev
> + * @regs: Camera ISP HW base register address
> + * @num_clks: The number of driver's clocks
> + * @clks: The clock data array
> + * @spinlock_irq: Used to protect register read/write data
> + * @enqueued_frame_seq_no: Frame sequence number of enqueued frame
> + * @dequeued_frame_seq_no: Frame sequence number of dequeued frame
> + * @composed_frame_seq_no: Frame sequence number of composed frame
> + * @timestamp: Frame timestamp in ns
> + * @sof_count: SOF counter
> + * @composer_wq: The work queue for frame request composing
> + * @composer_scp_addr: SCP address of ISP composer memory
> + * @composer_iova: DMA address of ISP composer memory
> + * @virt_addr: Virtual address of ISP composer memory
> + *
> + */
> +struct mtk_isp_p1_device {
> + struct device *dev;
> + struct mtk_scp *scp;
> + struct rproc *rproc_handle;
> + struct mtk_cam_dev cam_dev;
> + void __iomem *regs;
> + unsigned int num_clks;
> + struct clk_bulk_data *clks;
> + /* Used to protect register read/write data */
> + spinlock_t spinlock_irq;
> + unsigned int enqueued_frame_seq_no;
> + unsigned int dequeued_frame_seq_no;
> + unsigned int composed_frame_seq_no;
> + u8 sof_count;
> + struct workqueue_struct *composer_wq;
> + dma_addr_t composer_scp_addr;
> + dma_addr_t composer_iova;
> + void *composer_virt_addr;
> +};
> +
> +int mtk_isp_hw_init(struct mtk_cam_dev *cam_dev);
> +int mtk_isp_hw_release(struct mtk_cam_dev *cam_dev);
> +void mtk_isp_hw_config(struct mtk_cam_dev *cam_dev,
> + struct p1_config_param *config_param);
> +void mtk_isp_stream(struct mtk_cam_dev *cam_dev, int on);
> +void mtk_isp_req_enqueue(struct mtk_cam_dev *cam_dev,
> + struct mtk_cam_dev_request *req);
> +
> +#endif /* __MTK_CAM_HW_H__ */
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ipi.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ipi.h
> new file mode 100644
> index 000000000000..981b634dd91f
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-ipi.h
> @@ -0,0 +1,222 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_CAM_IPI_H__
> +#define __MTK_CAM_IPI_H__
> +
> +#include <linux/types.h>
> +
> +/*
> + * struct img_size - Image size information.
> + *
> + * @w: Image width, the unit is pixel
> + * @h: Image height, the unit is pixel
> + * @xsize: Bytes per line based on width.
> + * @stride: Bytes per line when changing line.
> + * Stride is based on xsize + HW constrain(byte align).
> + *
> + */
> +struct img_size {
> + u32 w;
> + u32 h;
> + u32 xsize;
> + u32 stride;
> +} __packed;
> +
> +/*
> + * struct p1_img_crop - image corp information
> + *
> + * @left: The left of crop area.
> + * @top: The top of crop area.
> + * @width: The width of crop area.
> + * @height: The height of crop area.
> + *
> + */
> +struct p1_img_crop {
> + u32 left;
> + u32 top;
> + u32 width;
> + u32 height;
> +} __packed;
> +
> +/*
> + * struct dma_buffer - DMA buffer address information
> + *
> + * @iova: DMA address for ISP DMA device
> + * @scp_addr: SCP address for external co-process unit
> + *
> + */
> +struct dma_buffer {
> + u32 iova;
> + u32 scp_addr;
> +} __packed;
> +
> +/*
> + * struct p1_img_output - ISP P1 image output information
> + *
> + * @buffer: DMA buffer address of image.
> + * @size: The image size configuration.
> + * @crop: The crop configuration.
> + * @pixel_bits: The bits per image pixel.
> + * @img_fmt: The image format.
> + *
> + */
> +struct p1_img_output {
> + struct dma_buffer buffer;
> + struct img_size size;
> + struct p1_img_crop crop;
> + u8 pixel_bits;
> + u32 img_fmt;
> +} __packed;
> +
> +/*
> + * struct cfg_in_param - Image input parameters structure.
> + * Normally, it comes from sensor information.
> + *
> + * @continuous: Indicate the sensor mode. Continuous or single shot.
> + * @subsample: Indicate to enables SOF subsample or not.
> + * @pixel_mode: Describe 1/2/4 pixels per clock cycle.
> + * @data_pattern: Describe input data pattern.
> + * @raw_pixel_id: Bayer sequence.
> + * @tg_fps: The fps rate of TG (time generator).
> + * @img_fmt: The image format of input source.
> + * @p1_img_crop: The crop configuration of input source.
> + *
> + */
> +struct cfg_in_param {
> + u8 continuous;
> + u8 subsample;
> + u8 pixel_mode;
> + u8 data_pattern;
> + u8 raw_pixel_id;
> + u16 tg_fps;
> + u32 img_fmt;
> + struct p1_img_crop crop;
> +} __packed;
> +
> +/*
> + * struct cfg_main_out_param - The image output parameters of main stream.
> + *
> + * @bypass: Indicate this device is enabled or disabled or not.
> + * @pure_raw: Indicate the image path control.
> + * True: pure raw
> + * False: processing raw
> + * @pure_raw_pack: Indicate the image is packed or not.
> + * True: packed mode
> + * False: unpacked mode
> + * @p1_img_output: The output image information.
> + *
> + */
> +struct cfg_main_out_param {
> + u8 bypass;
> + u8 pure_raw;
> + u8 pure_raw_pack;
> + struct p1_img_output output;
> +} __packed;
> +
> +/*
> + * struct cfg_resize_out_param - The image output parameters of
> + * packed out stream.
> + *
> + * @bypass: Indicate this device is enabled or disabled or not.
> + * @p1_img_output: The output image information.
> + *
> + */
> +struct cfg_resize_out_param {
> + u8 bypass;
> + struct p1_img_output output;
> +} __packed;
> +
> +/*
> + * struct p1_config_param - ISP P1 configuration parameters.
> + *
> + * @cfg_in_param: The Image input parameters.
> + * @cfg_main_param: The main output image parameters.
> + * @cfg_resize_out_param: The packed output image parameters.
> + * @enabled_dmas: The enabled DMA port information.
> + *
> + */
> +struct p1_config_param {
> + struct cfg_in_param cfg_in_param;
> + struct cfg_main_out_param cfg_main_param;
> + struct cfg_resize_out_param cfg_resize_param;
> + u32 enabled_dmas;
> +} __packed;
> +
> +/*
> + * struct P1_meta_frame - ISP P1 meta frame information.
> + *
> + * @enabled_dma: The enabled DMA port information.
> + * @vb_index: The VB2 index of meta buffer.
> + * @meta_addr: DMA buffer address of meta buffer.
> + *
> + */
> +struct P1_meta_frame {
> + u32 enabled_dma;
> + u32 vb_index;
> + struct dma_buffer meta_addr;
> +} __packed;
> +
> +/*
> + * struct isp_init_info - ISP P1 composer init information.
> + *
> + * @hw_module: The ISP Camera HW module ID.
> + * @cq_addr: The DMA address of composer memory.
> + *
> + */
> +struct isp_init_info {
> + u8 hw_module;
> + struct dma_buffer cq_addr;
> +} __packed;
> +
> +/*
> + * struct isp_ack_info - ISP P1 IPI command ack information.
> + *
> + * @cmd_id: The IPI command ID is acked.
> + * @frame_seq_no: The IPI frame sequence number is acked.
> + *
> + */
> +struct isp_ack_info {
> + u8 cmd_id;
> + u32 frame_seq_no;
> +} __packed;
> +
> +/*
> + * The IPI command enumeration.
> + */
> +enum mtk_isp_scp_cmds {
> + ISP_CMD_INIT,
> + ISP_CMD_CONFIG,
> + ISP_CMD_STREAM,
> + ISP_CMD_DEINIT,
> + ISP_CMD_ACK,
> + ISP_CMD_FRAME_ACK,
> + ISP_CMD_RESERVED,
> +};
> +
> +/*
> + * struct mtk_isp_scp_p1_cmd - ISP P1 IPI command strcture.
> + *
> + * @cmd_id: The IPI command ID.
> + * @init_param: The init formation for ISP_CMD_INIT.
> + * @config_param: The cmd configuration for ISP_CMD_CONFIG.
> + * @enabled_dmas: The meta configuration information for ISP_CMD_CONFIG_META.
> + * @is_stream_on: The stream information for ISP_CMD_STREAM.
> + * @ack_info: The cmd ack. information for ISP_CMD_ACK.
> + *
> + */
> +struct mtk_isp_scp_p1_cmd {
> + u8 cmd_id;
> + union {
> + struct isp_init_info init_param;
> + struct p1_config_param config_param;
> + u32 enabled_dmas;
> + struct P1_meta_frame meta_frame;
> + u8 is_stream_on;
> + struct isp_ack_info ack_info;
> + };
> +} __packed;
> +
> +#endif /* __MTK_CAM_IPI_H__ */
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> new file mode 100644
> index 000000000000..ab2277f45fa4
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam-regs.h
> @@ -0,0 +1,95 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_CAM_REGS_H__
> +#define __MTK_CAM_REGS_H__
> +
> +/* ISP interrupt enable */
> +#define REG_CTL_RAW_INT_EN 0x0020
> +#define DMA_ERR_INT_EN BIT(29)
> +
> +/* ISP interrupt status */
> +#define REG_CTL_RAW_INT_STAT 0x0024
> +#define VS_INT_ST BIT(0)
> +#define TG_ERR_ST BIT(4)
> +#define TG_GBERR_ST BIT(5)
> +#define CQ_CODE_ERR_ST BIT(6)
> +#define CQ_APB_ERR_ST BIT(7)
> +#define CQ_VS_ERR_ST BIT(8)
> +#define HW_PASS1_DON_ST BIT(11)
> +#define SOF_INT_ST BIT(12)
> +#define AMX_ERR_ST BIT(15)
> +#define RMX_ERR_ST BIT(16)
> +#define BMX_ERR_ST BIT(17)
> +#define RRZO_ERR_ST BIT(18)
> +#define AFO_ERR_ST BIT(19)
> +#define IMGO_ERR_ST BIT(20)
> +#define AAO_ERR_ST BIT(21)
> +#define PSO_ERR_ST BIT(22)
> +#define LCSO_ERR_ST BIT(23)
> +#define BNR_ERR_ST BIT(24)
> +#define LSCI_ERR_ST BIT(25)
> +#define DMA_ERR_ST BIT(29)
> +#define SW_PASS1_DON_ST BIT(30)
> +
> +/* ISP interrupt 2 status */
> +#define REG_CTL_RAW_INT2_STAT 0x0034
> +#define AFO_DONE_ST BIT(5)
> +#define AAO_DONE_ST BIT(7)
> +
> +/* Configures sensor mode */
> +#define REG_TG_SEN_MODE 0x0230
> +#define TG_SEN_MODE_CMOS_EN BIT(0)
> +
> +/* View finder mode control */
> +#define REG_TG_VF_CON 0x0234
> +#define TG_VF_CON_VFDATA_EN BIT(0)
> +
> +/* View finder mode control */
> +#define REG_TG_INTER_ST 0x026c
> +#define TG_CS_MASK 0x3f00
> +#define TG_IDLE_ST BIT(8)
> +
> +/* IMGO error status register */
> +#define REG_IMGO_ERR_STAT 0x1360
> +/* RRZO error status register */
> +#define REG_RRZO_ERR_STAT 0x1364
> +/* AAO error status register */
> +#define REG_AAO_ERR_STAT 0x1368
> +/* AFO error status register */
> +#define REG_AFO_ERR_STAT 0x136c
> +/* LCSO error status register */
> +#define REG_LCSO_ERR_STAT 0x1370
> +/* BPCI error status register */
> +#define REG_BPCI_ERR_STAT 0x137c
> +/* LSCI error status register */
> +#define REG_LSCI_ERR_STAT 0x1384
> +/* LMVO error status register */
> +#define REG_LMVO_ERR_STAT 0x1390
> +/* FLKO error status register */
> +#define REG_FLKO_ERR_STAT 0x1394
> +/* PSO error status register */
> +#define REG_PSO_ERR_STAT 0x13a0
> +
> +/* CQ0 base address */
> +#define REG_CQ_THR0_BASEADDR 0x0198
> +/* Frame sequence number */
> +#define REG_FRAME_SEQ_NUM 0x13b8
> +
> +/* IRQ Error Mask */
> +#define INT_ST_MASK_CAM_ERR ( \
> + TG_ERR_ST |\
> + TG_GBERR_ST |\
> + CQ_CODE_ERR_ST |\
> + CQ_APB_ERR_ST |\
> + CQ_VS_ERR_ST |\
> + BNR_ERR_ST |\
> + RMX_ERR_ST |\
> + BMX_ERR_ST |\
> + BNR_ERR_ST |\
> + LSCI_ERR_ST |\
> + DMA_ERR_ST)
> +
> +#endif /* __MTK_CAM_REGS_H__ */
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> new file mode 100644
> index 000000000000..23fdb8b4abc5
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.c
> @@ -0,0 +1,2087 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2019 MediaTek Inc.
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/of.h>
> +#include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/videodev2.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-mc.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "mtk_cam.h"
> +#include "mtk_cam-hw.h"
> +
> +#define R_IMGO BIT(0)
> +#define R_RRZO BIT(1)
> +#define R_AAO BIT(3)
> +#define R_AFO BIT(4)
> +#define R_LCSO BIT(5)
> +#define R_LMVO BIT(7)
> +#define R_FLKO BIT(8)
> +#define R_PSO BIT(10)
> +
> +#define MTK_ISP_ONE_PIXEL_MODE 1
> +#define MTK_ISP_MIN_RESIZE_RATIO 6
> +#define MTK_ISP_MAX_RUNNING_JOBS 3
> +
> +#define MTK_CAM_CIO_PAD_SRC 4
> +#define MTK_CAM_CIO_PAD_SINK 11
> +
> +static inline struct mtk_cam_video_device *
> +file_to_mtk_cam_node(struct file *__file)
> +{
> + return container_of(video_devdata(__file),
> + struct mtk_cam_video_device, vdev);
> +}
> +
> +static inline struct mtk_cam_video_device *
> +mtk_cam_vbq_to_vdev(struct vb2_queue *__vq)
> +{
> + return container_of(__vq, struct mtk_cam_video_device, vbq);
> +}
> +
> +static inline struct mtk_cam_dev_request *
> +mtk_cam_req_to_dev_req(struct media_request *__req)
> +{
> + return container_of(__req, struct mtk_cam_dev_request, req);
> +}
> +
> +static inline struct mtk_cam_dev_buffer *
> +mtk_cam_vb2_buf_to_dev_buf(struct vb2_buffer *__vb)
> +{
> + return container_of(__vb, struct mtk_cam_dev_buffer, vbb.vb2_buf);
> +}
> +
> +static void mtk_cam_dev_job_done(struct mtk_cam_dev *cam,
> + struct mtk_cam_dev_request *req,
> + enum vb2_buffer_state state)
> +{
> + struct media_request_object *obj, *obj_prev;
> + unsigned long flags;
> + u64 ts_eof = ktime_get_boottime_ns();
> +
> + if (!cam->streaming)
> + return;
> +
> + dev_dbg(cam->dev, "job done request:%s frame_seq:%d state:%d\n",
> + req->req.debug_str, req->frame_params.frame_seq_no, state);
> +
> + list_for_each_entry_safe(obj, obj_prev, &req->req.objects, list) {
> + struct vb2_buffer *vb;
> + struct mtk_cam_dev_buffer *buf;
> + struct mtk_cam_video_device *node;
> +
> + if (!vb2_request_object_is_buffer(obj))
> + continue;
> + vb = container_of(obj, struct vb2_buffer, req_obj);
> + buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> + node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> + spin_lock_irqsave(&node->buf_list_lock, flags);
> + list_del(&buf->list);
> + spin_unlock_irqrestore(&node->buf_list_lock, flags);
> + buf->vbb.sequence = req->frame_params.frame_seq_no;
> + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
> + vb->timestamp = ts_eof;
> + else
> + vb->timestamp = req->timestamp;
> + vb2_buffer_done(&buf->vbb.vb2_buf, state);
> + }
> +}
> +
> +struct mtk_cam_dev_request *mtk_cam_dev_get_req(struct mtk_cam_dev *cam,
> + unsigned int frame_seq_no)
> +{
> + struct mtk_cam_dev_request *req, *req_prev;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&cam->running_job_lock, flags);
> + list_for_each_entry_safe(req, req_prev, &cam->running_job_list, list) {
> + dev_dbg(cam->dev, "frame_seq:%d, get frame_seq:%d\n",
> + req->frame_params.frame_seq_no, frame_seq_no);
> +
> + /* Match by the en-queued request number */
> + if (req->frame_params.frame_seq_no == frame_seq_no) {
> + spin_unlock_irqrestore(&cam->running_job_lock, flags);
> + return req;
> + }
> + }
> + spin_unlock_irqrestore(&cam->running_job_lock, flags);
> +
> + return NULL;
> +}
> +
> +void mtk_cam_dev_dequeue_req_frame(struct mtk_cam_dev *cam,
> + unsigned int frame_seq_no)
> +{
> + struct mtk_cam_dev_request *req, *req_prev;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&cam->running_job_lock, flags);
> + list_for_each_entry_safe(req, req_prev, &cam->running_job_list, list) {
> + dev_dbg(cam->dev, "frame_seq:%d, de-queue frame_seq:%d\n",
> + req->frame_params.frame_seq_no, frame_seq_no);
> +
> + /* Match by the en-queued request number */
> + if (req->frame_params.frame_seq_no == frame_seq_no) {
> + cam->running_job_count--;
> + /* Pass to user space */
> + mtk_cam_dev_job_done(cam, req, VB2_BUF_STATE_DONE);
> + list_del(&req->list);
> + break;
> + } else if (req->frame_params.frame_seq_no < frame_seq_no) {
> + cam->running_job_count--;
> + /* Pass to user space for frame drop */
> + mtk_cam_dev_job_done(cam, req, VB2_BUF_STATE_ERROR);
Hi, I see that frame_params.frame_seq_no is incremented when a request is queued
and frame_seq_no is read from a register, so if the first is lower than the latter
it means userspace was queueing request too slowly and missed frames right?
So userspace will have to catch up in order not to get "ERROR" buffers.
Maybe the driver can just skip lost frames. In the rkisp1 for example,
if there is no buffer available, the driver just write to a dummy buffer that is
never sent to userspace. This way userspace always get valid buffers back when
dequeueing.
> + dev_warn(cam->dev, "frame_seq:%d drop\n",
> + req->frame_params.frame_seq_no);
> + list_del(&req->list);
> + } else {
> + break;
Does this case can ever occur?
Thanks,
Dafna
> + }
> + }
> + spin_unlock_irqrestore(&cam->running_job_lock, flags);
> +}
> +
> +static void mtk_cam_dev_req_cleanup(struct mtk_cam_dev *cam)
> +{
> + struct mtk_cam_dev_request *req, *req_prev;
> + unsigned long flags;
> +
> + dev_dbg(cam->dev, "%s\n", __func__);
> +
> + spin_lock_irqsave(&cam->pending_job_lock, flags);
> + list_for_each_entry_safe(req, req_prev, &cam->pending_job_list, list)
> + list_del(&req->list);
> + spin_unlock_irqrestore(&cam->pending_job_lock, flags);
> +
> + spin_lock_irqsave(&cam->running_job_lock, flags);
> + list_for_each_entry_safe(req, req_prev, &cam->running_job_list, list)
> + list_del(&req->list);
> + spin_unlock_irqrestore(&cam->running_job_lock, flags);
> +}
> +
> +void mtk_cam_dev_req_try_queue(struct mtk_cam_dev *cam)
> +{
> + struct mtk_cam_dev_request *req, *req_prev;
> + unsigned long flags;
> +
> + if (!cam->streaming) {
> + dev_dbg(cam->dev, "stream is off\n");
> + return;
> + }
> +
> + spin_lock_irqsave(&cam->pending_job_lock, flags);
> + spin_lock_irqsave(&cam->running_job_lock, flags);
> + list_for_each_entry_safe(req, req_prev, &cam->pending_job_list, list) {
> + if (cam->running_job_count >= MTK_ISP_MAX_RUNNING_JOBS) {
> + dev_dbg(cam->dev, "jobs are full\n");
> + break;
> + }
> + cam->running_job_count++;
> + list_del(&req->list);
> + list_add_tail(&req->list, &cam->running_job_list);
> + mtk_isp_req_enqueue(cam, req);
> + }
> + spin_unlock_irqrestore(&cam->running_job_lock, flags);
> + spin_unlock_irqrestore(&cam->pending_job_lock, flags);
> +}
> +
> +static struct media_request *mtk_cam_req_alloc(struct media_device *mdev)
> +{
> + struct mtk_cam_dev_request *cam_dev_req;
> +
> + cam_dev_req = kzalloc(sizeof(*cam_dev_req), GFP_KERNEL);
> +
> + return &cam_dev_req->req;
> +}
> +
> +static void mtk_cam_req_free(struct media_request *req)
> +{
> + struct mtk_cam_dev_request *cam_dev_req = mtk_cam_req_to_dev_req(req);
> +
> + kfree(cam_dev_req);
> +}
> +
> +static void mtk_cam_req_queue(struct media_request *req)
> +{
> + struct mtk_cam_dev_request *cam_req = mtk_cam_req_to_dev_req(req);
> + struct mtk_cam_dev *cam = container_of(req->mdev, struct mtk_cam_dev,
> + media_dev);
> + unsigned long flags;
> +
> + /* update frame_params's dma_bufs in mtk_cam_vb2_buf_queue */
> + vb2_request_queue(req);
> +
> + /* add to pending job list */
> + spin_lock_irqsave(&cam->pending_job_lock, flags);
> + list_add_tail(&cam_req->list, &cam->pending_job_list);
> + spin_unlock_irqrestore(&cam->pending_job_lock, flags);
> +
> + mtk_cam_dev_req_try_queue(cam);
> +}
> +
> +static unsigned int get_pixel_bits(unsigned int pix_fmt)
> +{
> + switch (pix_fmt) {
> + case V4L2_PIX_FMT_MTISP_SBGGR8:
> + case V4L2_PIX_FMT_MTISP_SGBRG8:
> + case V4L2_PIX_FMT_MTISP_SGRBG8:
> + case V4L2_PIX_FMT_MTISP_SRGGB8:
> + case V4L2_PIX_FMT_MTISP_SBGGR8F:
> + case V4L2_PIX_FMT_MTISP_SGBRG8F:
> + case V4L2_PIX_FMT_MTISP_SGRBG8F:
> + case V4L2_PIX_FMT_MTISP_SRGGB8F:
> + return 8;
> + case V4L2_PIX_FMT_MTISP_SBGGR10:
> + case V4L2_PIX_FMT_MTISP_SGBRG10:
> + case V4L2_PIX_FMT_MTISP_SGRBG10:
> + case V4L2_PIX_FMT_MTISP_SRGGB10:
> + case V4L2_PIX_FMT_MTISP_SBGGR10F:
> + case V4L2_PIX_FMT_MTISP_SGBRG10F:
> + case V4L2_PIX_FMT_MTISP_SGRBG10F:
> + case V4L2_PIX_FMT_MTISP_SRGGB10F:
> + return 10;
> + case V4L2_PIX_FMT_MTISP_SBGGR12:
> + case V4L2_PIX_FMT_MTISP_SGBRG12:
> + case V4L2_PIX_FMT_MTISP_SGRBG12:
> + case V4L2_PIX_FMT_MTISP_SRGGB12:
> + case V4L2_PIX_FMT_MTISP_SBGGR12F:
> + case V4L2_PIX_FMT_MTISP_SGBRG12F:
> + case V4L2_PIX_FMT_MTISP_SGRBG12F:
> + case V4L2_PIX_FMT_MTISP_SRGGB12F:
> + return 12;
> + case V4L2_PIX_FMT_MTISP_SBGGR14:
> + case V4L2_PIX_FMT_MTISP_SGBRG14:
> + case V4L2_PIX_FMT_MTISP_SGRBG14:
> + case V4L2_PIX_FMT_MTISP_SRGGB14:
> + case V4L2_PIX_FMT_MTISP_SBGGR14F:
> + case V4L2_PIX_FMT_MTISP_SGBRG14F:
> + case V4L2_PIX_FMT_MTISP_SGRBG14F:
> + case V4L2_PIX_FMT_MTISP_SRGGB14F:
> + return 14;
> + default:
> + return 0;
> + }
> +}
> +
> +static void cal_image_pix_mp(struct mtk_cam_dev *cam, unsigned int node_id,
> + struct v4l2_pix_format_mplane *mp)
> +{
> + unsigned int bpl, ppl;
> + unsigned int pixel_bits = get_pixel_bits(mp->pixelformat);
> + unsigned int width = mp->width;
> +
> + bpl = 0;
> + if (node_id == MTK_CAM_P1_MAIN_STREAM_OUT) {
> + /* Bayer encoding format & 2 bytes alignment */
> + bpl = ALIGN(DIV_ROUND_UP(width * pixel_bits, 8), 2);
> + } else if (node_id == MTK_CAM_P1_PACKED_BIN_OUT) {
> + /*
> + * The FULL-G encoding format
> + * 1 G component per pixel
> + * 1 R component per 4 pixel
> + * 1 B component per 4 pixel
> + * Total 4G/1R/1B in 4 pixel (pixel per line:ppl)
> + */
> + ppl = DIV_ROUND_UP(width * 6, 4);
> + bpl = DIV_ROUND_UP(ppl * pixel_bits, 8);
> +
> + /* 4 bytes alignment for 10 bit & others are 8 bytes */
> + if (pixel_bits == 10)
> + bpl = ALIGN(bpl, 4);
> + else
> + bpl = ALIGN(bpl, 8);
> + }
> + /*
> + * This image output buffer will be input buffer of MTK CAM DIP HW
> + * For MTK CAM DIP HW constrained, it needs 4 bytes alignment
> + */
> + bpl = ALIGN(bpl, 4);
> +
> + mp->plane_fmt[0].bytesperline = bpl;
> + mp->plane_fmt[0].sizeimage = bpl * mp->height;
> +
> + dev_dbg(cam->dev, "node:%d width:%d bytesperline:%d sizeimage:%d\n",
> + node_id, width, bpl, mp->plane_fmt[0].sizeimage);
> +}
> +
> +static const struct v4l2_format *
> +mtk_cam_dev_find_fmt(struct mtk_cam_dev_node_desc *desc, u32 format)
> +{
> + int i;
> + const struct v4l2_format *dev_fmt;
> +
> + for (i = 0; i < desc->num_fmts; i++) {
> + dev_fmt = &desc->fmts[i];
> + if (dev_fmt->fmt.pix_mp.pixelformat == format)
> + return dev_fmt;
> + }
> +
> + return NULL;
> +}
> +
> +/* Get the default format setting */
> +static void
> +mtk_cam_dev_load_default_fmt(struct mtk_cam_dev *cam,
> + struct mtk_cam_dev_node_desc *queue_desc,
> + struct v4l2_format *dest)
> +{
> + const struct v4l2_format *default_fmt =
> + &queue_desc->fmts[queue_desc->default_fmt_idx];
> +
> + dest->type = queue_desc->buf_type;
> +
> + /* Configure default format based on node type */
> + if (!queue_desc->image) {
> + dest->fmt.meta.dataformat = default_fmt->fmt.meta.dataformat;
> + dest->fmt.meta.buffersize = default_fmt->fmt.meta.buffersize;
> + return;
> + }
> +
> + dest->fmt.pix_mp.pixelformat = default_fmt->fmt.pix_mp.pixelformat;
> + dest->fmt.pix_mp.width = default_fmt->fmt.pix_mp.width;
> + dest->fmt.pix_mp.height = default_fmt->fmt.pix_mp.height;
> + /* bytesperline & sizeimage calculation */
> + cal_image_pix_mp(cam, queue_desc->id, &dest->fmt.pix_mp);
> + dest->fmt.pix_mp.num_planes = 1;
> +
> + dest->fmt.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
> + dest->fmt.pix_mp.field = V4L2_FIELD_NONE;
> + dest->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + dest->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> + dest->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
> +}
> +
> +/* Utility functions */
> +static unsigned int get_sensor_pixel_id(unsigned int fmt)
> +{
> + switch (fmt) {
> + case MEDIA_BUS_FMT_SBGGR8_1X8:
> + case MEDIA_BUS_FMT_SBGGR10_1X10:
> + case MEDIA_BUS_FMT_SBGGR12_1X12:
> + case MEDIA_BUS_FMT_SBGGR14_1X14:
> + return MTK_CAM_RAW_PXL_ID_B;
> + case MEDIA_BUS_FMT_SGBRG8_1X8:
> + case MEDIA_BUS_FMT_SGBRG10_1X10:
> + case MEDIA_BUS_FMT_SGBRG12_1X12:
> + case MEDIA_BUS_FMT_SGBRG14_1X14:
> + return MTK_CAM_RAW_PXL_ID_GB;
> + case MEDIA_BUS_FMT_SGRBG8_1X8:
> + case MEDIA_BUS_FMT_SGRBG10_1X10:
> + case MEDIA_BUS_FMT_SGRBG12_1X12:
> + case MEDIA_BUS_FMT_SGRBG14_1X14:
> + return MTK_CAM_RAW_PXL_ID_GR;
> + case MEDIA_BUS_FMT_SRGGB8_1X8:
> + case MEDIA_BUS_FMT_SRGGB10_1X10:
> + case MEDIA_BUS_FMT_SRGGB12_1X12:
> + case MEDIA_BUS_FMT_SRGGB14_1X14:
> + return MTK_CAM_RAW_PXL_ID_R;
> + default:
> + return MTK_CAM_RAW_PXL_ID_UNKNOWN;
> + }
> +}
> +
> +static unsigned int get_sensor_fmt(unsigned int fmt)
> +{
> + switch (fmt) {
> + case MEDIA_BUS_FMT_SBGGR8_1X8:
> + case MEDIA_BUS_FMT_SGBRG8_1X8:
> + case MEDIA_BUS_FMT_SGRBG8_1X8:
> + case MEDIA_BUS_FMT_SRGGB8_1X8:
> + return MTK_CAM_IMG_FMT_BAYER8;
> + case MEDIA_BUS_FMT_SBGGR10_1X10:
> + case MEDIA_BUS_FMT_SGBRG10_1X10:
> + case MEDIA_BUS_FMT_SGRBG10_1X10:
> + case MEDIA_BUS_FMT_SRGGB10_1X10:
> + return MTK_CAM_IMG_FMT_BAYER10;
> + case MEDIA_BUS_FMT_SBGGR12_1X12:
> + case MEDIA_BUS_FMT_SGBRG12_1X12:
> + case MEDIA_BUS_FMT_SGRBG12_1X12:
> + case MEDIA_BUS_FMT_SRGGB12_1X12:
> + return MTK_CAM_IMG_FMT_BAYER12;
> + case MEDIA_BUS_FMT_SBGGR14_1X14:
> + case MEDIA_BUS_FMT_SGBRG14_1X14:
> + case MEDIA_BUS_FMT_SGRBG14_1X14:
> + case MEDIA_BUS_FMT_SRGGB14_1X14:
> + return MTK_CAM_IMG_FMT_BAYER14;
> + default:
> + return MTK_CAM_IMG_FMT_UNKNOWN;
> + }
> +}
> +
> +static unsigned int get_img_fmt(unsigned int fourcc)
> +{
> + switch (fourcc) {
> + case V4L2_PIX_FMT_MTISP_SBGGR8:
> + case V4L2_PIX_FMT_MTISP_SGBRG8:
> + case V4L2_PIX_FMT_MTISP_SGRBG8:
> + case V4L2_PIX_FMT_MTISP_SRGGB8:
> + return MTK_CAM_IMG_FMT_BAYER8;
> + case V4L2_PIX_FMT_MTISP_SBGGR8F:
> + case V4L2_PIX_FMT_MTISP_SGBRG8F:
> + case V4L2_PIX_FMT_MTISP_SGRBG8F:
> + case V4L2_PIX_FMT_MTISP_SRGGB8F:
> + return MTK_CAM_IMG_FMT_FG_BAYER8;
> + case V4L2_PIX_FMT_MTISP_SBGGR10:
> + case V4L2_PIX_FMT_MTISP_SGBRG10:
> + case V4L2_PIX_FMT_MTISP_SGRBG10:
> + case V4L2_PIX_FMT_MTISP_SRGGB10:
> + return MTK_CAM_IMG_FMT_BAYER10;
> + case V4L2_PIX_FMT_MTISP_SBGGR10F:
> + case V4L2_PIX_FMT_MTISP_SGBRG10F:
> + case V4L2_PIX_FMT_MTISP_SGRBG10F:
> + case V4L2_PIX_FMT_MTISP_SRGGB10F:
> + return MTK_CAM_IMG_FMT_FG_BAYER10;
> + case V4L2_PIX_FMT_MTISP_SBGGR12:
> + case V4L2_PIX_FMT_MTISP_SGBRG12:
> + case V4L2_PIX_FMT_MTISP_SGRBG12:
> + case V4L2_PIX_FMT_MTISP_SRGGB12:
> + return MTK_CAM_IMG_FMT_BAYER12;
> + case V4L2_PIX_FMT_MTISP_SBGGR12F:
> + case V4L2_PIX_FMT_MTISP_SGBRG12F:
> + case V4L2_PIX_FMT_MTISP_SGRBG12F:
> + case V4L2_PIX_FMT_MTISP_SRGGB12F:
> + return MTK_CAM_IMG_FMT_FG_BAYER12;
> + case V4L2_PIX_FMT_MTISP_SBGGR14:
> + case V4L2_PIX_FMT_MTISP_SGBRG14:
> + case V4L2_PIX_FMT_MTISP_SGRBG14:
> + case V4L2_PIX_FMT_MTISP_SRGGB14:
> + return MTK_CAM_IMG_FMT_BAYER14;
> + case V4L2_PIX_FMT_MTISP_SBGGR14F:
> + case V4L2_PIX_FMT_MTISP_SGBRG14F:
> + case V4L2_PIX_FMT_MTISP_SGRBG14F:
> + case V4L2_PIX_FMT_MTISP_SRGGB14F:
> + return MTK_CAM_IMG_FMT_FG_BAYER14;
> + default:
> + return MTK_CAM_IMG_FMT_UNKNOWN;
> + }
> +}
> +
> +static int config_img_fmt(struct mtk_cam_dev *cam, unsigned int node_id,
> + struct p1_img_output *out_fmt, int sd_width,
> + int sd_height)
> +{
> + const struct v4l2_format *cfg_fmt = &cam->vdev_nodes[node_id].vdev_fmt;
> +
> + /* Check output & input image size dimension */
> + if (cfg_fmt->fmt.pix_mp.width > sd_width ||
> + cfg_fmt->fmt.pix_mp.height > sd_height) {
> + dev_err(cam->dev, "node:%d cfg size is larger than sensor\n",
> + node_id);
> + return -EINVAL;
> + }
> +
> + /* Check resize ratio for resize out stream due to HW constraint */
> + if (((cfg_fmt->fmt.pix_mp.width * 100 / sd_width) <
> + MTK_ISP_MIN_RESIZE_RATIO) ||
> + ((cfg_fmt->fmt.pix_mp.height * 100 / sd_height) <
> + MTK_ISP_MIN_RESIZE_RATIO)) {
> + dev_err(cam->dev, "node:%d resize ratio is less than %d%%\n",
> + node_id, MTK_ISP_MIN_RESIZE_RATIO);
> + return -EINVAL;
> + }
> +
> + out_fmt->img_fmt = get_img_fmt(cfg_fmt->fmt.pix_mp.pixelformat);
> + out_fmt->pixel_bits = get_pixel_bits(cfg_fmt->fmt.pix_mp.pixelformat);
> + if (out_fmt->img_fmt == MTK_CAM_IMG_FMT_UNKNOWN ||
> + !out_fmt->pixel_bits) {
> + dev_err(cam->dev, "node:%d unknown pixel fmt:%d\n",
> + node_id, cfg_fmt->fmt.pix_mp.pixelformat);
> + return -EINVAL;
> + }
> + dev_dbg(cam->dev, "node:%d pixel_bits:%d img_fmt:0x%x\n",
> + node_id, out_fmt->pixel_bits, out_fmt->img_fmt);
> +
> + out_fmt->size.w = cfg_fmt->fmt.pix_mp.width;
> + out_fmt->size.h = cfg_fmt->fmt.pix_mp.height;
> + out_fmt->size.stride = cfg_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> + out_fmt->size.xsize = cfg_fmt->fmt.pix_mp.plane_fmt[0].bytesperline;
> +
> + out_fmt->crop.left = 0;
> + out_fmt->crop.top = 0;
> + out_fmt->crop.width = sd_width;
> + out_fmt->crop.height = sd_height;
> +
> + dev_dbg(cam->dev,
> + "node:%d size=%0dx%0d, stride:%d, xsize:%d, crop=%0dx%0d\n",
> + node_id, out_fmt->size.w, out_fmt->size.h,
> + out_fmt->size.stride, out_fmt->size.xsize,
> + out_fmt->crop.width, out_fmt->crop.height);
> +
> + return 0;
> +}
> +
> +static void mtk_cam_dev_init_stream(struct mtk_cam_dev *cam)
> +{
> + int i;
> +
> + cam->enabled_count = 0;
> + cam->enabled_dmas = 0;
> + cam->stream_count = 0;
> + cam->running_job_count = 0;
> +
> + /* Get the enabled meta DMA ports */
> + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) {
> + if (!cam->vdev_nodes[i].enabled)
> + continue;
> + cam->enabled_count++;
> + cam->enabled_dmas |= cam->vdev_nodes[i].desc.dma_port;
> + }
> +
> + dev_dbg(cam->dev, "%s:%d:0x%x\n", __func__, cam->enabled_count,
> + cam->enabled_dmas);
> +}
> +
> +static int mtk_cam_dev_isp_config(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + struct p1_config_param config_param;
> + struct cfg_in_param *cfg_in_param;
> + struct v4l2_subdev_format sd_fmt;
> + int sd_width, sd_height, sd_code;
> + unsigned int enabled_dma_ports = cam->enabled_dmas;
> + int ret;
> +
> + /* Get sensor format configuration */
> + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> + ret = v4l2_subdev_call(cam->sensor, pad, get_fmt, NULL, &sd_fmt);
> + if (ret) {
> + dev_dbg(dev, "sensor g_fmt failed:%d\n", ret);
> + return ret;
> + }
> + sd_width = sd_fmt.format.width;
> + sd_height = sd_fmt.format.height;
> + sd_code = sd_fmt.format.code;
> + dev_dbg(dev, "sd fmt w*h=%d*%d, code=0x%x\n", sd_width, sd_height,
> + sd_code);
> +
> + memset(&config_param, 0, sizeof(config_param));
> +
> + /* Update cfg_in_param */
> + cfg_in_param = &config_param.cfg_in_param;
> + cfg_in_param->continuous = true;
> + /* Fix to one pixel mode in default */
> + cfg_in_param->pixel_mode = MTK_ISP_ONE_PIXEL_MODE;
> + cfg_in_param->crop.width = sd_width;
> + cfg_in_param->crop.height = sd_height;
> + cfg_in_param->raw_pixel_id = get_sensor_pixel_id(sd_code);
> + cfg_in_param->img_fmt = get_sensor_fmt(sd_code);
> + if (cfg_in_param->img_fmt == MTK_CAM_IMG_FMT_UNKNOWN ||
> + cfg_in_param->raw_pixel_id == MTK_CAM_RAW_PXL_ID_UNKNOWN) {
> + dev_err(dev, "unknown sd code:%d\n", sd_code);
> + return -EINVAL;
> + }
> +
> + /* Update cfg_main_param */
> + config_param.cfg_main_param.pure_raw = true;
> + config_param.cfg_main_param.pure_raw_pack = true;
> + ret = config_img_fmt(cam, MTK_CAM_P1_MAIN_STREAM_OUT,
> + &config_param.cfg_main_param.output,
> + sd_width, sd_height);
> + if (ret)
> + return ret;
> +
> + /* Update cfg_resize_param */
> + if (enabled_dma_ports & R_RRZO) {
> + ret = config_img_fmt(cam, MTK_CAM_P1_PACKED_BIN_OUT,
> + &config_param.cfg_resize_param.output,
> + sd_width, sd_height);
> + if (ret)
> + return ret;
> + } else {
> + config_param.cfg_resize_param.bypass = true;
> + }
> +
> + /* Update enabled_dmas */
> + config_param.enabled_dmas = enabled_dma_ports;
> + mtk_isp_hw_config(cam, &config_param);
> + dev_dbg(dev, "%s done\n", __func__);
> +
> + return 0;
> +}
> +
> +void mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam,
> + unsigned int frame_seq_no)
> +{
> + struct v4l2_event event = {
> + .type = V4L2_EVENT_FRAME_SYNC,
> + .u.frame_sync.frame_sequence = frame_seq_no,
> + };
> +
> + v4l2_event_queue(cam->subdev.devnode, &event);
> +}
> +
> +static struct v4l2_subdev *
> +mtk_cam_cio_get_active_sensor(struct mtk_cam_dev *cam)
> +{
> + struct media_device *mdev = cam->seninf->entity.graph_obj.mdev;
> + struct device *dev = cam->dev;
> + struct media_entity *entity;
> + struct v4l2_subdev *sensor;
> +
> + sensor = NULL;
> + media_device_for_each_entity(entity, mdev) {
> + dev_dbg(dev, "media entity: %s:0x%x:%d\n",
> + entity->name, entity->function, entity->stream_count);
> + if (entity->function == MEDIA_ENT_F_CAM_SENSOR &&
> + entity->stream_count) {
> + sensor = media_entity_to_v4l2_subdev(entity);
> + dev_dbg(dev, "sensor found: %s\n", entity->name);
> + break;
> + }
> + }
> +
> + if (!sensor)
> + dev_err(dev, "no seninf connected\n");
> +
> + return sensor;
> +}
> +
> +static int mtk_cam_cio_stream_on(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + int ret;
> +
> + if (!cam->seninf) {
> + dev_err(dev, "no seninf connected\n");
> + return -ENODEV;
> + }
> +
> + /* Get active sensor from graph topology */
> + cam->sensor = mtk_cam_cio_get_active_sensor(cam);
> + if (!cam->sensor)
> + return -ENODEV;
> +
> + /* Seninf must stream on first */
> + ret = v4l2_subdev_call(cam->seninf, video, s_stream, 1);
> + if (ret) {
> + dev_err(dev, "failed to stream on %s:%d\n",
> + cam->seninf->entity.name, ret);
> + return ret;
> + }
> +
> + ret = v4l2_subdev_call(cam->sensor, video, s_stream, 1);
> + if (ret) {
> + dev_err(dev, "failed to stream on %s:%d\n",
> + cam->sensor->entity.name, ret);
> + goto fail_seninf_off;
> + }
> +
> + ret = mtk_cam_dev_isp_config(cam);
> + if (ret)
> + goto fail_sensor_off;
> +
> + cam->streaming = true;
> + mtk_isp_stream(cam, 1);
> + mtk_cam_dev_req_try_queue(cam);
> + dev_dbg(dev, "streamed on Pass 1\n");
> +
> + return 0;
> +
> +fail_sensor_off:
> + v4l2_subdev_call(cam->sensor, video, s_stream, 0);
> +fail_seninf_off:
> + v4l2_subdev_call(cam->seninf, video, s_stream, 0);
> +
> + return ret;
> +}
> +
> +static int mtk_cam_cio_stream_off(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + int ret;
> +
> + ret = v4l2_subdev_call(cam->sensor, video, s_stream, 0);
> + if (ret) {
> + dev_err(dev, "failed to stream off %s:%d\n",
> + cam->sensor->entity.name, ret);
> + return -EPERM;
> + }
> +
> + ret = v4l2_subdev_call(cam->seninf, video, s_stream, 0);
> + if (ret) {
> + dev_err(dev, "failed to stream off %s:%d\n",
> + cam->seninf->entity.name, ret);
> + return -EPERM;
> + }
> +
> + cam->streaming = false;
> + mtk_isp_stream(cam, 0);
> + mtk_isp_hw_release(cam);
> +
> + dev_dbg(dev, "streamed off Pass 1\n");
> +
> + return 0;
> +}
> +
> +static int mtk_cam_sd_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct mtk_cam_dev *cam = container_of(sd, struct mtk_cam_dev, subdev);
> +
> + if (enable) {
> + /* Align vb2_core_streamon design */
> + if (cam->streaming) {
> + dev_warn(cam->dev, "already streaming on\n");
> + return 0;
> + }
> + return mtk_cam_cio_stream_on(cam);
> + }
> +
> + if (!cam->streaming) {
> + dev_warn(cam->dev, "already streaming off\n");
> + return 0;
> + }
> + return mtk_cam_cio_stream_off(cam);
> +}
> +
> +static int mtk_cam_sd_subscribe_event(struct v4l2_subdev *subdev,
> + struct v4l2_fh *fh,
> + struct v4l2_event_subscription *sub)
> +{
> + switch (sub->type) {
> + case V4L2_EVENT_FRAME_SYNC:
> + return v4l2_event_subscribe(fh, sub, 0, NULL);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int mtk_cam_media_link_setup(struct media_entity *entity,
> + const struct media_pad *local,
> + const struct media_pad *remote, u32 flags)
> +{
> + struct mtk_cam_dev *cam =
> + container_of(entity, struct mtk_cam_dev, subdev.entity);
> + u32 pad = local->index;
> +
> + dev_dbg(cam->dev, "%s: %d->%d flags:0x%x\n",
> + __func__, pad, remote->index, flags);
> +
> + /*
> + * The video nodes exposed by the driver have pads indexes
> + * from 0 to MTK_CAM_P1_TOTAL_NODES - 1.
> + */
> + if (pad < MTK_CAM_P1_TOTAL_NODES)
> + cam->vdev_nodes[pad].enabled =
> + !!(flags & MEDIA_LNK_FL_ENABLED);
> +
> + return 0;
> +}
> +
> +static void mtk_cam_vb2_buf_queue(struct vb2_buffer *vb)
> +{
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vb->vb2_queue);
> + struct mtk_cam_dev_buffer *buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> + struct mtk_cam_dev_request *req = mtk_cam_req_to_dev_req(vb->request);
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> + struct device *dev = cam->dev;
> + unsigned long flags;
> +
> + dev_dbg(dev, "%s: node:%d fd:%d idx:%d\n", __func__,
> + node->id, buf->vbb.request_fd, buf->vbb.vb2_buf.index);
> +
> + /* added the buffer into the tracking list */
> + spin_lock_irqsave(&node->buf_list_lock, flags);
> + list_add_tail(&buf->list, &node->buf_list);
> + spin_unlock_irqrestore(&node->buf_list_lock, flags);
> +
> + /* update buffer internal address */
> + req->frame_params.dma_bufs[buf->node_id].iova = buf->daddr;
> + req->frame_params.dma_bufs[buf->node_id].scp_addr = buf->scp_addr;
> +}
> +
> +static int mtk_cam_vb2_buf_init(struct vb2_buffer *vb)
> +{
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vb->vb2_queue);
> + struct device *dev = cam->dev;
> + struct mtk_cam_dev_buffer *buf;
> + dma_addr_t addr;
> +
> + buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> + buf->node_id = node->id;
> + buf->daddr = vb2_dma_contig_plane_dma_addr(vb, 0);
> + buf->scp_addr = 0;
> +
> + /* SCP address is only valid for meta input buffer */
> + if (!node->desc.smem_alloc)
> + return 0;
> +
> + buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> + /* Use coherent address to get iova address */
> + addr = dma_map_resource(dev, buf->daddr, vb->planes[0].length,
> + DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
> + if (dma_mapping_error(dev, addr)) {
> + dev_err(dev, "failed to map meta addr:%pad\n", &buf->daddr);
> + return -EFAULT;
> + }
> + buf->scp_addr = buf->daddr;
> + buf->daddr = addr;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vb2_buf_prepare(struct vb2_buffer *vb)
> +{
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vb->vb2_queue);
> + struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
> + const struct v4l2_format *fmt = &node->vdev_fmt;
> + unsigned int size;
> +
> + if (vb->vb2_queue->type == V4L2_BUF_TYPE_META_OUTPUT ||
> + vb->vb2_queue->type == V4L2_BUF_TYPE_META_CAPTURE)
> + size = fmt->fmt.meta.buffersize;
> + else
> + size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
> +
> + if (vb2_plane_size(vb, 0) < size) {
> + dev_dbg(cam->dev, "plane size is too small:%lu<%u\n",
> + vb2_plane_size(vb, 0), size);
> + return -EINVAL;
> + }
> +
> + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
> + if (vb2_get_plane_payload(vb, 0) != size) {
> + dev_dbg(cam->dev, "plane payload is mismatch:%lu:%u\n",
> + vb2_get_plane_payload(vb, 0), size);
> + return -EINVAL;
> + }
> + return 0;
> + }
> +
> + v4l2_buf->field = V4L2_FIELD_NONE;
> + vb2_set_plane_payload(vb, 0, size);
> +
> + return 0;
> +}
> +
> +static void mtk_cam_vb2_buf_cleanup(struct vb2_buffer *vb)
> +{
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vb->vb2_queue);
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vb->vb2_queue);
> + struct mtk_cam_dev_buffer *buf;
> + struct device *dev = cam->dev;
> +
> + if (!node->desc.smem_alloc)
> + return;
> +
> + buf = mtk_cam_vb2_buf_to_dev_buf(vb);
> + dma_unmap_page_attrs(dev, buf->daddr,
> + vb->planes[0].length,
> + DMA_BIDIRECTIONAL,
> + DMA_ATTR_SKIP_CPU_SYNC);
> +}
> +
> +static void mtk_cam_vb2_request_complete(struct vb2_buffer *vb)
> +{
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vb->vb2_queue);
> +
> + dev_dbg(cam->dev, "%s\n", __func__);
> +}
> +
> +static int mtk_cam_vb2_queue_setup(struct vb2_queue *vq,
> + unsigned int *num_buffers,
> + unsigned int *num_planes,
> + unsigned int sizes[],
> + struct device *alloc_devs[])
> +{
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> + unsigned int max_buffer_count = node->desc.max_buf_count;
> + const struct v4l2_format *fmt = &node->vdev_fmt;
> + unsigned int size;
> +
> + /* Check the limitation of buffer size */
> + if (max_buffer_count)
> + *num_buffers = clamp_val(*num_buffers, 1, max_buffer_count);
> +
> + if (node->desc.smem_alloc)
> + vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
> +
> + if (vq->type == V4L2_BUF_TYPE_META_OUTPUT ||
> + vq->type == V4L2_BUF_TYPE_META_CAPTURE)
> + size = fmt->fmt.meta.buffersize;
> + else
> + size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
> +
> + /* Add for q.create_bufs with fmt.g_sizeimage(p) / 2 test */
> + if (*num_planes) {
> + if (sizes[0] < size || *num_planes != 1)
> + return -EINVAL;
> + } else {
> + *num_planes = 1;
> + sizes[0] = size;
> + }
> +
> + return 0;
> +}
> +
> +static void mtk_cam_vb2_return_all_buffers(struct mtk_cam_dev *cam,
> + struct mtk_cam_video_device *node,
> + enum vb2_buffer_state state)
> +{
> + struct mtk_cam_dev_buffer *buf, *buf_prev;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&node->buf_list_lock, flags);
> + list_for_each_entry_safe(buf, buf_prev, &node->buf_list, list) {
> + list_del(&buf->list);
> + vb2_buffer_done(&buf->vbb.vb2_buf, state);
> + }
> + spin_unlock_irqrestore(&node->buf_list_lock, flags);
> +}
> +
> +static int mtk_cam_vb2_start_streaming(struct vb2_queue *vq,
> + unsigned int count)
> +{
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vq);
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> + struct device *dev = cam->dev;
> + int ret;
> +
> + if (!node->enabled) {
> + dev_err(dev, "Node:%d is not enabled\n", node->id);
> + ret = -ENOLINK;
> + goto fail_ret_buf;
> + }
> +
> + mutex_lock(&cam->op_lock);
> + /* Start streaming of the whole pipeline now*/
> + if (!cam->pipeline.streaming_count) {
> + ret = media_pipeline_start(&node->vdev.entity, &cam->pipeline);
> + if (ret) {
> + dev_err(dev, "failed to start pipeline:%d\n", ret);
> + goto fail_unlock;
> + }
> + mtk_cam_dev_init_stream(cam);
> + ret = mtk_isp_hw_init(cam);
> + if (ret) {
> + dev_err(dev, "failed to init HW:%d\n", ret);
> + goto fail_stop_pipeline;
> + }
> + }
> +
> + /* Media links are fixed after media_pipeline_start */
> + cam->stream_count++;
> + dev_dbg(dev, "%s: count info:%d:%d\n", __func__, cam->stream_count,
> + cam->enabled_count);
> + if (cam->stream_count < cam->enabled_count) {
> + mutex_unlock(&cam->op_lock);
> + return 0;
> + }
> +
> + /* Stream on sub-devices node */
> + ret = v4l2_subdev_call(&cam->subdev, video, s_stream, 1);
> + if (ret)
> + goto fail_no_stream;
> + mutex_unlock(&cam->op_lock);
> +
> + return 0;
> +
> +fail_no_stream:
> + cam->stream_count--;
> +fail_stop_pipeline:
> + if (cam->stream_count == 0)
> + media_pipeline_stop(&node->vdev.entity);
> +fail_unlock:
> + mutex_unlock(&cam->op_lock);
> +fail_ret_buf:
> + mtk_cam_vb2_return_all_buffers(cam, node, VB2_BUF_STATE_QUEUED);
> +
> + return ret;
> +}
> +
> +static void mtk_cam_vb2_stop_streaming(struct vb2_queue *vq)
> +{
> + struct mtk_cam_dev *cam = vb2_get_drv_priv(vq);
> + struct mtk_cam_video_device *node = mtk_cam_vbq_to_vdev(vq);
> + struct device *dev = cam->dev;
> +
> + mutex_lock(&cam->op_lock);
> + dev_dbg(dev, "%s node:%d count info:%d\n", __func__, node->id,
> + cam->stream_count);
> + /* Check the first node to stream-off */
> + if (cam->stream_count == cam->enabled_count)
> + v4l2_subdev_call(&cam->subdev, video, s_stream, 0);
> +
> + mtk_cam_vb2_return_all_buffers(cam, node, VB2_BUF_STATE_ERROR);
> + cam->stream_count--;
> + if (cam->stream_count) {
> + mutex_unlock(&cam->op_lock);
> + return;
> + }
> + mutex_unlock(&cam->op_lock);
> +
> + mtk_cam_dev_req_cleanup(cam);
> + media_pipeline_stop(&node->vdev.entity);
> +}
> +
> +static int mtk_cam_vidioc_querycap(struct file *file, void *fh,
> + struct v4l2_capability *cap)
> +{
> + struct mtk_cam_dev *cam = video_drvdata(file);
> +
> + strscpy(cap->driver, dev_driver_string(cam->dev), sizeof(cap->driver));
> + strscpy(cap->card, dev_driver_string(cam->dev), sizeof(cap->card));
> + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
> + dev_name(cam->dev));
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_enum_fmt(struct file *file, void *fh,
> + struct v4l2_fmtdesc *f)
> +{
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> + if (f->index >= node->desc.num_fmts)
> + return -EINVAL;
> +
> + /* f->description is filled in v4l_fill_fmtdesc function */
> + f->pixelformat = node->desc.fmts[f->index].fmt.pix_mp.pixelformat;
> + f->flags = 0;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_g_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> + f->fmt = node->vdev_fmt.fmt;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_try_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct mtk_cam_dev *cam = video_drvdata(file);
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> + struct device *dev = cam->dev;
> + const struct v4l2_format *dev_fmt;
> + struct v4l2_format try_fmt;
> +
> + memset(&try_fmt, 0, sizeof(try_fmt));
> + try_fmt.type = f->type;
> +
> + /* Validate pixelformat */
> + dev_fmt = mtk_cam_dev_find_fmt(&node->desc, f->fmt.pix_mp.pixelformat);
> + if (!dev_fmt) {
> + dev_dbg(dev, "unknown fmt:%d\n", f->fmt.pix_mp.pixelformat);
> + dev_fmt = &node->desc.fmts[node->desc.default_fmt_idx];
> + }
> + try_fmt.fmt.pix_mp.pixelformat = dev_fmt->fmt.pix_mp.pixelformat;
> +
> + /* Validate image width & height range */
> + try_fmt.fmt.pix_mp.width = clamp_val(f->fmt.pix_mp.width,
> + IMG_MIN_WIDTH, IMG_MAX_WIDTH);
> + try_fmt.fmt.pix_mp.height = clamp_val(f->fmt.pix_mp.height,
> + IMG_MIN_HEIGHT, IMG_MAX_HEIGHT);
> + /* 4 bytes alignment for width */
> + try_fmt.fmt.pix_mp.width = ALIGN(try_fmt.fmt.pix_mp.width, 4);
> +
> + /* Only support one plane */
> + try_fmt.fmt.pix_mp.num_planes = 1;
> +
> + /* bytesperline & sizeimage calculation */
> + cal_image_pix_mp(cam, node->id, &try_fmt.fmt.pix_mp);
> +
> + /* Constant format fields */
> + try_fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
> + try_fmt.fmt.pix_mp.field = V4L2_FIELD_NONE;
> + try_fmt.fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> + try_fmt.fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> + try_fmt.fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB;
> +
> + *f = try_fmt;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_s_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct mtk_cam_dev *cam = video_drvdata(file);
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> + if (vb2_is_busy(node->vdev.queue)) {
> + dev_dbg(cam->dev, "%s: queue is busy\n", __func__);
> + return -EBUSY;
> + }
> +
> + /* Get the valid format */
> + mtk_cam_vidioc_try_fmt(file, fh, f);
> + /* Configure to video device */
> + node->vdev_fmt = *f;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_enum_framesizes(struct file *filp, void *priv,
> + struct v4l2_frmsizeenum *sizes)
> +{
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(filp);
> + const struct v4l2_format *dev_fmt;
> +
> + dev_fmt = mtk_cam_dev_find_fmt(&node->desc, sizes->pixel_format);
> + if (!dev_fmt || sizes->index)
> + return -EINVAL;
> +
> + sizes->type = node->desc.frmsizes->type;
> + memcpy(&sizes->stepwise, &node->desc.frmsizes->stepwise,
> + sizeof(sizes->stepwise));
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_meta_enum_fmt(struct file *file, void *fh,
> + struct v4l2_fmtdesc *f)
> +{
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> + if (f->index)
> + return -EINVAL;
> +
> + /* f->description is filled in v4l_fill_fmtdesc function */
> + f->pixelformat = node->vdev_fmt.fmt.meta.dataformat;
> + f->flags = 0;
> +
> + return 0;
> +}
> +
> +static int mtk_cam_vidioc_g_meta_fmt(struct file *file, void *fh,
> + struct v4l2_format *f)
> +{
> + struct mtk_cam_video_device *node = file_to_mtk_cam_node(file);
> +
> + f->fmt.meta.dataformat = node->vdev_fmt.fmt.meta.dataformat;
> + f->fmt.meta.buffersize = node->vdev_fmt.fmt.meta.buffersize;
> +
> + return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops mtk_cam_subdev_core_ops = {
> + .subscribe_event = mtk_cam_sd_subscribe_event,
> + .unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
> +static const struct v4l2_subdev_video_ops mtk_cam_subdev_video_ops = {
> + .s_stream = mtk_cam_sd_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mtk_cam_subdev_ops = {
> + .core = &mtk_cam_subdev_core_ops,
> + .video = &mtk_cam_subdev_video_ops,
> +};
> +
> +static const struct media_entity_operations mtk_cam_media_entity_ops = {
> + .link_setup = mtk_cam_media_link_setup,
> + .link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static const struct vb2_ops mtk_cam_vb2_ops = {
> + .queue_setup = mtk_cam_vb2_queue_setup,
> + .wait_prepare = vb2_ops_wait_prepare,
> + .wait_finish = vb2_ops_wait_finish,
> + .buf_init = mtk_cam_vb2_buf_init,
> + .buf_prepare = mtk_cam_vb2_buf_prepare,
> + .start_streaming = mtk_cam_vb2_start_streaming,
> + .stop_streaming = mtk_cam_vb2_stop_streaming,
> + .buf_queue = mtk_cam_vb2_buf_queue,
> + .buf_cleanup = mtk_cam_vb2_buf_cleanup,
> + .buf_request_complete = mtk_cam_vb2_request_complete,
> +};
> +
> +static const struct v4l2_file_operations mtk_cam_v4l2_fops = {
> + .unlocked_ioctl = video_ioctl2,
> + .open = v4l2_fh_open,
> + .release = vb2_fop_release,
> + .poll = vb2_fop_poll,
> + .mmap = vb2_fop_mmap,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl32 = v4l2_compat_ioctl32,
> +#endif
> +};
> +
> +static const struct media_device_ops mtk_cam_media_ops = {
> + .req_alloc = mtk_cam_req_alloc,
> + .req_free = mtk_cam_req_free,
> + .req_validate = vb2_request_validate,
> + .req_queue = mtk_cam_req_queue,
> +};
> +
> +static int mtk_cam_media_register(struct mtk_cam_dev *cam,
> + struct media_device *media_dev)
> +{
> + /* Reserved MTK_CAM_CIO_PAD_SINK + 1 pads to use */
> + unsigned int num_pads = MTK_CAM_CIO_PAD_SINK + 1;
> + struct device *dev = cam->dev;
> + int i, ret;
> +
> + media_dev->dev = cam->dev;
> + strscpy(media_dev->model, dev_driver_string(dev),
> + sizeof(media_dev->model));
> + snprintf(media_dev->bus_info, sizeof(media_dev->bus_info),
> + "platform:%s", dev_name(dev));
> + media_dev->hw_revision = 0;
> + media_device_init(media_dev);
> + media_dev->ops = &mtk_cam_media_ops;
> +
> + ret = media_device_register(media_dev);
> + if (ret) {
> + dev_err(dev, "failed to register media device:%d\n", ret);
> + return ret;
> + }
> +
> + /* Initialize subdev pads */
> + cam->subdev_pads = devm_kcalloc(dev, num_pads,
> + sizeof(*cam->subdev_pads),
> + GFP_KERNEL);
> + if (!cam->subdev_pads) {
> + dev_err(dev, "failed to allocate subdev_pads\n");
> + ret = -ENOMEM;
> + goto fail_media_unreg;
> + }
> +
> + ret = media_entity_pads_init(&cam->subdev.entity, num_pads,
> + cam->subdev_pads);
> + if (ret) {
> + dev_err(dev, "failed to initialize media pads:%d\n", ret);
> + goto fail_media_unreg;
> + }
> +
> + /* Initialize all pads with MEDIA_PAD_FL_SOURCE */
> + for (i = 0; i < num_pads; i++)
> + cam->subdev_pads[i].flags = MEDIA_PAD_FL_SOURCE;
> +
> + /* Customize the last one pad as CIO sink pad. */
> + cam->subdev_pads[MTK_CAM_CIO_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +
> + return 0;
> +
> +fail_media_unreg:
> + media_device_unregister(&cam->media_dev);
> + media_device_cleanup(&cam->media_dev);
> +
> + return ret;
> +}
> +
> +static int
> +mtk_cam_video_register_device(struct mtk_cam_dev *cam,
> + struct mtk_cam_video_device *node)
> +{
> + struct device *dev = cam->dev;
> + struct video_device *vdev = &node->vdev;
> + struct vb2_queue *vbq = &node->vbq;
> + unsigned int output = V4L2_TYPE_IS_OUTPUT(node->desc.buf_type);
> + unsigned int link_flags = node->desc.link_flags;
> + int ret;
> +
> + /* Initialize mtk_cam_video_device */
> + if (link_flags & MEDIA_LNK_FL_IMMUTABLE)
> + node->enabled = true;
> + else
> + node->enabled = false;
> + mtk_cam_dev_load_default_fmt(cam, &node->desc, &node->vdev_fmt);
> +
> + cam->subdev_pads[node->id].flags = output ?
> + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
> +
> + /* Initialize media entities */
> + ret = media_entity_pads_init(&vdev->entity, 1, &node->vdev_pad);
> + if (ret) {
> + dev_err(dev, "failed to initialize media pad:%d\n", ret);
> + return ret;
> + }
> + node->vdev_pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
> +
> + /* Initialize vbq */
> + vbq->type = node->desc.buf_type;
> + if (vbq->type == V4L2_BUF_TYPE_META_OUTPUT)
> + vbq->io_modes = VB2_MMAP;
> + else
> + vbq->io_modes = VB2_MMAP | VB2_DMABUF;
> +
> + if (node->desc.smem_alloc) {
> + vbq->bidirectional = 1;
> + vbq->dev = cam->smem_dev;
> + } else {
> + vbq->dev = dev;
> + }
> + vbq->ops = &mtk_cam_vb2_ops;
> + vbq->mem_ops = &vb2_dma_contig_memops;
> + vbq->buf_struct_size = sizeof(struct mtk_cam_dev_buffer);
> + vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_BOOTIME;
> + if (output)
> + vbq->timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
> + else
> + vbq->timestamp_flags |= V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
> + /* No minimum buffers limitation */
> + vbq->min_buffers_needed = 0;
> + vbq->drv_priv = cam;
> + vbq->lock = &node->vdev_lock;
> + vbq->supports_requests = true;
> + vbq->requires_requests = true;
> +
> + ret = vb2_queue_init(vbq);
> + if (ret) {
> + dev_err(dev, "failed to init. vb2 queue:%d\n", ret);
> + goto fail_media_clean;
> + }
> +
> + /* Initialize vdev */
> + snprintf(vdev->name, sizeof(vdev->name), "%s %s",
> + dev_driver_string(dev), node->desc.name);
> + /* set cap/type/ioctl_ops of the video device */
> + vdev->device_caps = node->desc.cap | V4L2_CAP_STREAMING;
> + vdev->ioctl_ops = node->desc.ioctl_ops;
> + vdev->fops = &mtk_cam_v4l2_fops;
> + vdev->release = video_device_release_empty;
> + vdev->lock = &node->vdev_lock;
> + vdev->v4l2_dev = &cam->v4l2_dev;
> + vdev->queue = &node->vbq;
> + vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
> + vdev->entity.function = MEDIA_ENT_F_IO_V4L;
> + vdev->entity.ops = NULL;
> + video_set_drvdata(vdev, cam);
> + dev_dbg(dev, "registered vdev:%d:%s\n", node->id, vdev->name);
> +
> + /* Initialize miscellaneous variables */
> + mutex_init(&node->vdev_lock);
> + INIT_LIST_HEAD(&node->buf_list);
> + spin_lock_init(&node->buf_list_lock);
> +
> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> + if (ret) {
> + dev_err(dev, "failed to register vde:%d\n", ret);
> + goto fail_vb2_rel;
> + }
> +
> + /* Create link between video node and the subdev pad */
> + if (output) {
> + ret = media_create_pad_link(&vdev->entity, 0,
> + &cam->subdev.entity,
> + node->id, link_flags);
> + } else {
> + ret = media_create_pad_link(&cam->subdev.entity,
> + node->id, &vdev->entity, 0,
> + link_flags);
> + }
> + if (ret)
> + goto fail_vdev_ureg;
> +
> + return 0;
> +
> +fail_vdev_ureg:
> + video_unregister_device(vdev);
> +fail_vb2_rel:
> + mutex_destroy(&node->vdev_lock);
> + vb2_queue_release(vbq);
> +fail_media_clean:
> + media_entity_cleanup(&vdev->entity);
> +
> + return ret;
> +}
> +
> +static void
> +mtk_cam_video_unregister_device(struct mtk_cam_video_device *node)
> +{
> + video_unregister_device(&node->vdev);
> + vb2_queue_release(&node->vbq);
> + media_entity_cleanup(&node->vdev.entity);
> + mutex_destroy(&node->vdev_lock);
> +}
> +
> +static int mtk_cam_v4l2_register(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + int i, ret;
> +
> + /* Set up media device & pads */
> + ret = mtk_cam_media_register(cam, &cam->media_dev);
> + if (ret)
> + return ret;
> + dev_info(dev, "Registered media%d\n", cam->media_dev.devnode->minor);
> +
> + /* Set up v4l2 device */
> + cam->v4l2_dev.mdev = &cam->media_dev;
> + ret = v4l2_device_register(dev, &cam->v4l2_dev);
> + if (ret) {
> + dev_err(dev, "failed to register V4L2 device:%d\n", ret);
> + goto fail_media_unreg;
> + }
> + dev_info(dev, "Registered %s\n", cam->v4l2_dev.name);
> +
> + /* Initialize subdev */
> + v4l2_subdev_init(&cam->subdev, &mtk_cam_subdev_ops);
> + cam->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
> + cam->subdev.entity.ops = &mtk_cam_media_entity_ops;
> + cam->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
> + V4L2_SUBDEV_FL_HAS_EVENTS;
> + snprintf(cam->subdev.name, sizeof(cam->subdev.name),
> + "%s", dev_driver_string(dev));
> + v4l2_set_subdevdata(&cam->subdev, cam);
> +
> + ret = v4l2_device_register_subdev(&cam->v4l2_dev, &cam->subdev);
> + if (ret) {
> + dev_err(dev, "failed to initialize subdev:%d\n", ret);
> + goto fail_clean_media_entiy;
> + }
> + dev_dbg(dev, "registered %s\n", cam->subdev.name);
> +
> + /* Create video nodes and links */
> + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++) {
> + struct mtk_cam_video_device *node = &cam->vdev_nodes[i];
> +
> + node->id = node->desc.id;
> + ret = mtk_cam_video_register_device(cam, node);
> + if (ret)
> + goto fail_vdev_unreg;
> + }
> + vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
> +
> + return 0;
> +
> +fail_vdev_unreg:
> + for (i--; i >= 0; i--)
> + mtk_cam_video_unregister_device(&cam->vdev_nodes[i]);
> +fail_clean_media_entiy:
> + media_entity_cleanup(&cam->subdev.entity);
> + v4l2_device_unregister(&cam->v4l2_dev);
> +fail_media_unreg:
> + media_device_unregister(&cam->media_dev);
> + media_device_cleanup(&cam->media_dev);
> +
> + return ret;
> +}
> +
> +static int mtk_cam_v4l2_unregister(struct mtk_cam_dev *cam)
> +{
> + int i;
> +
> + for (i = 0; i < MTK_CAM_P1_TOTAL_NODES; i++)
> + mtk_cam_video_unregister_device(&cam->vdev_nodes[i]);
> +
> + vb2_dma_contig_clear_max_seg_size(cam->dev);
> + v4l2_device_unregister_subdev(&cam->subdev);
> + v4l2_device_unregister(&cam->v4l2_dev);
> + media_entity_cleanup(&cam->subdev.entity);
> + media_device_unregister(&cam->media_dev);
> + media_device_cleanup(&cam->media_dev);
> +
> + return 0;
> +}
> +
> +static int mtk_cam_dev_notifier_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mtk_cam_dev *cam =
> + container_of(notifier, struct mtk_cam_dev, notifier);
> +
> + if (!(sd->entity.function & MEDIA_ENT_F_VID_IF_BRIDGE)) {
> + dev_dbg(cam->dev, "no MEDIA_ENT_F_VID_IF_BRIDGE function\n");
> + return -ENODEV;
> + }
> +
> + cam->seninf = sd;
> + dev_dbg(cam->dev, "%s is bound\n", sd->entity.name);
> +
> + return 0;
> +}
> +
> +static void mtk_cam_dev_notifier_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mtk_cam_dev *cam =
> + container_of(notifier, struct mtk_cam_dev, notifier);
> +
> + cam->seninf = NULL;
> + dev_dbg(cam->dev, "%s is unbound\n", sd->entity.name);
> +}
> +
> +static int mtk_cam_dev_notifier_complete(struct v4l2_async_notifier *notifier)
> +{
> + struct mtk_cam_dev *cam =
> + container_of(notifier, struct mtk_cam_dev, notifier);
> + struct device *dev = cam->dev;
> + int ret;
> +
> + if (!cam->seninf) {
> + dev_err(dev, "No seninf subdev\n");
> + return -ENODEV;
> + }
> +
> + ret = media_create_pad_link(&cam->seninf->entity, MTK_CAM_CIO_PAD_SRC,
> + &cam->subdev.entity, MTK_CAM_CIO_PAD_SINK,
> + MEDIA_LNK_FL_IMMUTABLE |
> + MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + dev_err(dev, "failed to create pad link %s %s err:%d\n",
> + cam->seninf->entity.name, cam->subdev.entity.name,
> + ret);
> + return ret;
> + }
> +
> + ret = v4l2_device_register_subdev_nodes(&cam->v4l2_dev);
> + if (ret) {
> + dev_err(dev, "failed to initialize subdev nodes:%d\n", ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static const struct v4l2_async_notifier_operations mtk_cam_v4l2_async_ops = {
> + .bound = mtk_cam_dev_notifier_bound,
> + .unbind = mtk_cam_dev_notifier_unbind,
> + .complete = mtk_cam_dev_notifier_complete,
> +};
> +
> +static int mtk_cam_v4l2_async_register(struct mtk_cam_dev *cam)
> +{
> + struct device *dev = cam->dev;
> + int ret;
> +
> + v4l2_async_notifier_init(&cam->notifier);
> + ret = v4l2_async_notifier_parse_fwnode_endpoints(dev,
> + &cam->notifier, sizeof(struct v4l2_async_subdev), NULL);
> + if (ret) {
> + dev_err(dev, "failed to parse fwnode endpoints:%d\n", ret);
> + return ret;
> + }
> +
> + cam->notifier.ops = &mtk_cam_v4l2_async_ops;
> + dev_dbg(dev, "mtk_cam v4l2_async_notifier_register\n");
> + ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier);
> + if (ret) {
> + dev_err(dev, "failed to register async notifier : %d\n", ret);
> + v4l2_async_notifier_cleanup(&cam->notifier);
> + }
> +
> + return ret;
> +}
> +
> +static void mtk_cam_v4l2_async_unregister(struct mtk_cam_dev *cam)
> +{
> + v4l2_async_notifier_unregister(&cam->notifier);
> + v4l2_async_notifier_cleanup(&cam->notifier);
> +}
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_vcap_ioctl_ops = {
> + .vidioc_querycap = mtk_cam_vidioc_querycap,
> + .vidioc_enum_framesizes = mtk_cam_vidioc_enum_framesizes,
> + .vidioc_enum_fmt_vid_cap = mtk_cam_vidioc_enum_fmt,
> + .vidioc_g_fmt_vid_cap_mplane = mtk_cam_vidioc_g_fmt,
> + .vidioc_s_fmt_vid_cap_mplane = mtk_cam_vidioc_s_fmt,
> + .vidioc_try_fmt_vid_cap_mplane = mtk_cam_vidioc_try_fmt,
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
> + .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
> +};
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_cap_ioctl_ops = {
> + .vidioc_querycap = mtk_cam_vidioc_querycap,
> + .vidioc_enum_fmt_meta_cap = mtk_cam_vidioc_meta_enum_fmt,
> + .vidioc_g_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_s_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_try_fmt_meta_cap = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> +};
> +
> +static const struct v4l2_ioctl_ops mtk_cam_v4l2_meta_out_ioctl_ops = {
> + .vidioc_querycap = mtk_cam_vidioc_querycap,
> + .vidioc_enum_fmt_meta_out = mtk_cam_vidioc_meta_enum_fmt,
> + .vidioc_g_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_s_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_try_fmt_meta_out = mtk_cam_vidioc_g_meta_fmt,
> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
> + .vidioc_querybuf = vb2_ioctl_querybuf,
> + .vidioc_qbuf = vb2_ioctl_qbuf,
> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
> + .vidioc_streamon = vb2_ioctl_streamon,
> + .vidioc_streamoff = vb2_ioctl_streamoff,
> + .vidioc_expbuf = vb2_ioctl_expbuf,
> +};
> +
> +static const struct v4l2_format meta_fmts[] = {
> + {
> + .fmt.meta = {
> + .dataformat = V4L2_META_FMT_MTISP_PARAMS,
> + .buffersize = 512 * SZ_1K,
> + },
> + },
> + {
> + .fmt.meta = {
> + .dataformat = V4L2_META_FMT_MTISP_3A,
> + .buffersize = 1200 * SZ_1K,
> + },
> + },
> + {
> + .fmt.meta = {
> + .dataformat = V4L2_META_FMT_MTISP_AF,
> + .buffersize = 640 * SZ_1K,
> + },
> + },
> + {
> + .fmt.meta = {
> + .dataformat = V4L2_META_FMT_MTISP_LCS,
> + .buffersize = 288 * SZ_1K,
> + },
> + },
> + {
> + .fmt.meta = {
> + .dataformat = V4L2_META_FMT_MTISP_LMV,
> + .buffersize = 256,
> + },
> + },
> +};
> +
> +static const struct v4l2_format stream_out_fmts[] = {
> + /* This is a default image format */
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR10,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR8,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR12,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR14,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG8,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG10,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG12,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG14,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG8,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG10,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG12,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG14,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB8,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB10,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB12,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB14,
> + },
> + },
> +};
> +
> +static const struct v4l2_format bin_out_fmts[] = {
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR8F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR10F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR12F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SBGGR14F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG8F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG10F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG12F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGBRG14F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG8F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG10F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG12F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SGRBG14F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB8F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB10F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB12F,
> + },
> + },
> + {
> + .fmt.pix_mp = {
> + .width = IMG_MAX_WIDTH,
> + .height = IMG_MAX_HEIGHT,
> + .pixelformat = V4L2_PIX_FMT_MTISP_SRGGB14F,
> + },
> + },
> +};
> +
> +static const struct
> +mtk_cam_dev_node_desc output_queues[] = {
> + {
> + .id = MTK_CAM_P1_META_IN_0,
> + .name = "meta input",
> + .cap = V4L2_CAP_META_OUTPUT,
> + .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
> + .link_flags = 0,
> + .image = false,
> + .smem_alloc = true,
> + .fmts = meta_fmts,
> + .default_fmt_idx = 0,
> + .max_buf_count = 10,
> + .ioctl_ops = &mtk_cam_v4l2_meta_out_ioctl_ops,
> + },
> +};
> +
> +static const struct
> +mtk_cam_dev_node_desc capture_queues[] = {
> + {
> + .id = MTK_CAM_P1_MAIN_STREAM_OUT,
> + .name = "main stream",
> + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> + .link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED,
> + .image = true,
> + .smem_alloc = false,
> + .dma_port = R_IMGO,
> + .fmts = stream_out_fmts,
> + .num_fmts = ARRAY_SIZE(stream_out_fmts),
> + .default_fmt_idx = 0,
> + .ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> + .frmsizes = &(struct v4l2_frmsizeenum) {
> + .index = 0,
> + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
> + .stepwise = {
> + .max_width = IMG_MAX_WIDTH,
> + .min_width = IMG_MIN_WIDTH,
> + .max_height = IMG_MAX_HEIGHT,
> + .min_height = IMG_MIN_HEIGHT,
> + .step_height = 1,
> + .step_width = 1,
> + },
> + },
> + },
> + {
> + .id = MTK_CAM_P1_PACKED_BIN_OUT,
> + .name = "packed out",
> + .cap = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
> + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
> + .link_flags = 0,
> + .image = true,
> + .smem_alloc = false,
> + .dma_port = R_RRZO,
> + .fmts = bin_out_fmts,
> + .num_fmts = ARRAY_SIZE(bin_out_fmts),
> + .default_fmt_idx = 0,
> + .ioctl_ops = &mtk_cam_v4l2_vcap_ioctl_ops,
> + .frmsizes = &(struct v4l2_frmsizeenum) {
> + .index = 0,
> + .type = V4L2_FRMSIZE_TYPE_CONTINUOUS,
> + .stepwise = {
> + .max_width = IMG_MAX_WIDTH,
> + .min_width = IMG_MIN_WIDTH,
> + .max_height = IMG_MAX_HEIGHT,
> + .min_height = IMG_MIN_HEIGHT,
> + .step_height = 1,
> + .step_width = 1,
> + },
> + },
> + },
> + {
> + .id = MTK_CAM_P1_META_OUT_0,
> + .name = "partial meta 0",
> + .cap = V4L2_CAP_META_CAPTURE,
> + .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> + .link_flags = 0,
> + .image = false,
> + .smem_alloc = false,
> + .dma_port = R_AAO | R_FLKO | R_PSO,
> + .fmts = meta_fmts,
> + .default_fmt_idx = 1,
> + .max_buf_count = 5,
> + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> + },
> + {
> + .id = MTK_CAM_P1_META_OUT_1,
> + .name = "partial meta 1",
> + .cap = V4L2_CAP_META_CAPTURE,
> + .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> + .link_flags = 0,
> + .image = false,
> + .smem_alloc = false,
> + .dma_port = R_AFO,
> + .fmts = meta_fmts,
> + .default_fmt_idx = 2,
> + .max_buf_count = 5,
> + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> + },
> + {
> + .id = MTK_CAM_P1_META_OUT_2,
> + .name = "partial meta 2",
> + .cap = V4L2_CAP_META_CAPTURE,
> + .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> + .link_flags = 0,
> + .image = false,
> + .smem_alloc = false,
> + .dma_port = R_LCSO,
> + .fmts = meta_fmts,
> + .default_fmt_idx = 3,
> + .max_buf_count = 10,
> + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> + },
> + {
> + .id = MTK_CAM_P1_META_OUT_3,
> + .name = "partial meta 3",
> + .cap = V4L2_CAP_META_CAPTURE,
> + .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
> + .link_flags = 0,
> + .image = false,
> + .smem_alloc = false,
> + .dma_port = R_LMVO,
> + .fmts = meta_fmts,
> + .default_fmt_idx = 4,
> + .max_buf_count = 10,
> + .ioctl_ops = &mtk_cam_v4l2_meta_cap_ioctl_ops,
> + },
> +};
> +
> +/* The helper to configure the device context */
> +static void mtk_cam_dev_queue_setup(struct mtk_cam_dev *cam)
> +{
> + unsigned int node_idx;
> + int i;
> +
> + node_idx = 0;
> + /* Setup the output queue */
> + for (i = 0; i < ARRAY_SIZE(output_queues); i++)
> + cam->vdev_nodes[node_idx++].desc = output_queues[i];
> +
> + /* Setup the capture queue */
> + for (i = 0; i < ARRAY_SIZE(capture_queues); i++)
> + cam->vdev_nodes[node_idx++].desc = capture_queues[i];
> +}
> +
> +int mtk_cam_dev_init(struct platform_device *pdev,
> + struct mtk_cam_dev *cam)
> +{
> + int ret;
> +
> + cam->dev = &pdev->dev;
> + mtk_cam_dev_queue_setup(cam);
> +
> + spin_lock_init(&cam->pending_job_lock);
> + spin_lock_init(&cam->running_job_lock);
> + INIT_LIST_HEAD(&cam->pending_job_list);
> + INIT_LIST_HEAD(&cam->running_job_list);
> + mutex_init(&cam->op_lock);
> +
> + /* v4l2 sub-device registration */
> + ret = mtk_cam_v4l2_register(cam);
> + if (ret)
> + return ret;
> +
> + ret = mtk_cam_v4l2_async_register(cam);
> + if (ret)
> + goto fail_v4l2_unreg;
> +
> + return 0;
> +
> +fail_v4l2_unreg:
> + mutex_destroy(&cam->op_lock);
> + mtk_cam_v4l2_unregister(cam);
> +
> + return ret;
> +}
> +
> +void mtk_cam_dev_cleanup(struct mtk_cam_dev *cam)
> +{
> + mtk_cam_v4l2_async_unregister(cam);
> + mtk_cam_v4l2_unregister(cam);
> + mutex_destroy(&cam->op_lock);
> +}
> +
> diff --git a/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> new file mode 100644
> index 000000000000..0a340a1e65ea
> --- /dev/null
> +++ b/drivers/media/platform/mtk-isp/isp_50/cam/mtk_cam.h
> @@ -0,0 +1,244 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 MediaTek Inc.
> + */
> +
> +#ifndef __MTK_CAM_H__
> +#define __MTK_CAM_H__
> +
> +#include <linux/device.h>
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-v4l2.h>
> +
> +#include "mtk_cam-ipi.h"
> +
> +#define IMG_MAX_WIDTH 5376
> +#define IMG_MAX_HEIGHT 4032
> +#define IMG_MIN_WIDTH 80
> +#define IMG_MIN_HEIGHT 60
> +
> +/*
> + * ID enum value for struct mtk_cam_dev_node_desc:id
> + * or mtk_cam_video_device:id
> + */
> +enum {
> + MTK_CAM_P1_META_IN_0 = 0,
> + MTK_CAM_P1_MAIN_STREAM_OUT,
> + MTK_CAM_P1_PACKED_BIN_OUT,
> + MTK_CAM_P1_META_OUT_0,
> + MTK_CAM_P1_META_OUT_1,
> + MTK_CAM_P1_META_OUT_2,
> + MTK_CAM_P1_META_OUT_3,
> + MTK_CAM_P1_TOTAL_NODES
> +};
> +
> +/* Supported image format list */
> +#define MTK_CAM_IMG_FMT_UNKNOWN 0x0000
> +#define MTK_CAM_IMG_FMT_BAYER8 0x2200
> +#define MTK_CAM_IMG_FMT_BAYER10 0x2201
> +#define MTK_CAM_IMG_FMT_BAYER12 0x2202
> +#define MTK_CAM_IMG_FMT_BAYER14 0x2203
> +#define MTK_CAM_IMG_FMT_FG_BAYER8 0x2204
> +#define MTK_CAM_IMG_FMT_FG_BAYER10 0x2205
> +#define MTK_CAM_IMG_FMT_FG_BAYER12 0x2206
> +#define MTK_CAM_IMG_FMT_FG_BAYER14 0x2207
> +
> +/* Supported bayer pixel order */
> +#define MTK_CAM_RAW_PXL_ID_B 0
> +#define MTK_CAM_RAW_PXL_ID_GB 1
> +#define MTK_CAM_RAW_PXL_ID_GR 2
> +#define MTK_CAM_RAW_PXL_ID_R 3
> +#define MTK_CAM_RAW_PXL_ID_UNKNOWN 4
> +
> +/*
> + * struct mtk_p1_frame_param - MTK ISP P1 driver frame parameters.
> + *
> + * @frame_seq_no: The frame sequence of frame in driver layer.
> + * @dma_bufs: The DMA buffer address information of enabled DMA nodes.
> + *
> + */
> +struct mtk_p1_frame_param {
> + unsigned int frame_seq_no;
> + struct dma_buffer dma_bufs[MTK_CAM_P1_TOTAL_NODES];
> +} __packed;
> +
> +/*
> + * struct mtk_cam_dev_request - MTK camera device request.
> + *
> + * @req: Embedded struct media request.
> + * @frame_params: The frame info. & address info. of enabled DMA nodes.
> + * @frame_work: work queue entry for frame transmission to SCP.
> + * @list: List entry of the object for @struct mtk_cam_dev:
> + * pending_job_list or running_job_list.
> + * @timestamp: Start of frame timestamp in ns
> + *
> + */
> +struct mtk_cam_dev_request {
> + struct media_request req;
> + struct mtk_p1_frame_param frame_params;
> + struct work_struct frame_work;
> + struct list_head list;
> + u64 timestamp;
> +};
> +
> +/*
> + * struct mtk_cam_dev_buffer - MTK camera device buffer.
> + *
> + * @vbb: Embedded struct vb2_v4l2_buffer.
> + * @list: List entry of the object for @struct mtk_cam_video_device:
> + * buf_list.
> + * @daddr: The DMA address of this buffer.
> + * @scp_addr: The SCP address of this buffer which
> + * is only supported for meta input node.
> + * @node_id: The vidoe node id which this buffer belongs to.
> + *
> + */
> +struct mtk_cam_dev_buffer {
> + struct vb2_v4l2_buffer vbb;
> + struct list_head list;
> + /* Intenal part */
> + dma_addr_t daddr;
> + dma_addr_t scp_addr;
> + unsigned int node_id;
> +};
> +
> +/*
> + * struct mtk_cam_dev_node_desc - MTK camera device node descriptor
> + *
> + * @id: id of the node
> + * @name: name of the node
> + * @cap: supported V4L2 capabilities
> + * @buf_type: supported V4L2 buffer type
> + * @dma_port: the dma ports associated to the node
> + * @link_flags: default media link flags
> + * @smem_alloc: using the smem_dev as alloc device or not
> + * @image: true for image node, false for meta node
> + * @num_fmts: the number of supported node formats
> + * @default_fmt_idx: default format of this node
> + * @max_buf_count: maximum VB2 buffer count
> + * @ioctl_ops: mapped to v4l2_ioctl_ops
> + * @fmts: supported format
> + * @frmsizes: supported V4L2 frame size number
> + *
> + */
> +struct mtk_cam_dev_node_desc {
> + u8 id;
> + const char *name;
> + u32 cap;
> + u32 buf_type;
> + u32 dma_port;
> + u32 link_flags;
> + u8 smem_alloc:1;
> + u8 image:1;
> + u8 num_fmts;
> + u8 default_fmt_idx;
> + u8 max_buf_count;
> + const struct v4l2_ioctl_ops *ioctl_ops;
> + const struct v4l2_format *fmts;
> + const struct v4l2_frmsizeenum *frmsizes;
> +};
> +
> +/*
> + * struct mtk_cam_video_device - Mediatek video device structure
> + *
> + * @id: Id for index of mtk_cam_dev:vdev_nodes array
> + * @enabled: Indicate the video device is enabled or not
> + * @desc: The node description of video device
> + * @vdev_fmt: The V4L2 format of video device
> + * @vdev_pad: The media pad graph object of video device
> + * @vbq: A videobuf queue of video device
> + * @vdev: The video device instance
> + * @vdev_lock: Serializes vb2 queue and video device operations
> + * @buf_list: List for enqueue buffers
> + * @buf_list_lock: Lock used to protect buffer list.
> + *
> + */
> +struct mtk_cam_video_device {
> + unsigned int id;
> + unsigned int enabled;
> + struct mtk_cam_dev_node_desc desc;
> + struct v4l2_format vdev_fmt;
> + struct media_pad vdev_pad;
> + struct vb2_queue vbq;
> + struct video_device vdev;
> + /* Serializes vb2 queue and video device operations */
> + struct mutex vdev_lock;
> + struct list_head buf_list;
> + /* Lock used to protect buffer list */
> + spinlock_t buf_list_lock;
> +};
> +
> +/*
> + * struct mtk_cam_dev - Mediatek camera device structure.
> + *
> + * @dev: Pointer to device.
> + * @smem_pdev: Pointer to shared memory device.
> + * @pipeline: Media pipeline information.
> + * @media_dev: Media device instance.
> + * @subdev: The V4L2 sub-device instance.
> + * @v4l2_dev: The V4L2 device driver instance.
> + * @notifier: The v4l2_device notifier data.
> + * @subdev_pads: Pointer to the number of media pads of this sub-device.
> + * @vdev_nodes: The array list of mtk_cam_video_device nodes.
> + * @seninf: Pointer to the seninf sub-device.
> + * @sensor: Pointer to the active sensor V4L2 sub-device when streaming on.
> + * @streaming: Indicate the overall streaming status is on or off.
> + * @enabled_dmas: The enabled dma port information when streaming on.
> + * @enabled_count: Number of enabled video nodes
> + * @stream_count: Number of streaming video nodes
> + * @running_job_count: Nunber of running jobs in the HW driver.
> + * @pending_job_list: List to keep the media requests before en-queue into
> + * HW driver.
> + * @pending_job_lock: Protect the pending_job_list data & running_job_count.
> + * @running_job_list: List to keep the media requests after en-queue into
> + * HW driver.
> + * @running_job_lock: Protect the running_job_list data.
> + * @op_lock: Serializes driver's VB2 callback operations.
> + *
> + */
> +struct mtk_cam_dev {
> + struct device *dev;
> + struct device *smem_dev;
> + struct media_pipeline pipeline;
> + struct media_device media_dev;
> + struct v4l2_subdev subdev;
> + struct v4l2_device v4l2_dev;
> + struct v4l2_async_notifier notifier;
> + struct media_pad *subdev_pads;
> + struct mtk_cam_video_device vdev_nodes[MTK_CAM_P1_TOTAL_NODES];
> + struct v4l2_subdev *seninf;
> + struct v4l2_subdev *sensor;
> + unsigned int streaming;
> + unsigned int enabled_dmas;
> + unsigned int enabled_count;
> + unsigned int stream_count;
> + unsigned int running_job_count;
> + struct list_head pending_job_list;
> + /* Protect the pending_job_list data */
> + spinlock_t pending_job_lock;
> + struct list_head running_job_list;
> + /* Protect the running_job_list data & running_job_count */
> + spinlock_t running_job_lock;
> + /* Serializes driver's VB2 callback operations */
> + struct mutex op_lock;
> +};
> +
> +int mtk_cam_dev_init(struct platform_device *pdev,
> + struct mtk_cam_dev *cam_dev);
> +void mtk_cam_dev_cleanup(struct mtk_cam_dev *cam_dev);
> +void mtk_cam_dev_req_try_queue(struct mtk_cam_dev *cam_dev);
> +void mtk_cam_dev_dequeue_req_frame(struct mtk_cam_dev *cam_dev,
> + unsigned int frame_seq_no);
> +void mtk_cam_dev_event_frame_sync(struct mtk_cam_dev *cam_dev,
> + unsigned int frame_seq_no);
> +struct mtk_cam_dev_request *mtk_cam_dev_get_req(struct mtk_cam_dev *cam,
> + unsigned int frame_seq_no);
> +
> +#endif /* __MTK_CAM_H__ */
>
^ permalink raw reply
* Re: [PATCH 1/4] dt-bindings: phy: phy-rockchip-dphy-rx0: move rockchip dphy rx0 bindings out of staging
From: Ezequiel Garcia @ 2020-04-02 16:17 UTC (permalink / raw)
To: Helen Koike, devicetree, linux-media, linux-rockchip,
Kishon Vijay Abraham I
Cc: linux-kernel, devel, robh+dt, heiko, hverkuil-cisco, kernel,
dafna.hirschfeld, mark.rutland, karthik.poduval
In-Reply-To: <20200402000234.226466-2-helen.koike@collabora.com>
(+Kishon)
Hi Helen,
I was wondering if we couldn't also move the phy driver out of staging.
Thanks,
Ezequiel
On Wed, 2020-04-01 at 21:02 -0300, Helen Koike wrote:
> Move phy-rockchip-dphy-rx0 bindings to Documentation/devicetree/bindings/phy
>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> ---
> .../devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml | 0
> 1 file changed, 0 insertions(+), 0 deletions(-)
> rename {drivers/staging/media/phy-rockchip-dphy-rx0/Documentation => Documentation}/devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml (100%)
>
> diff --git a/drivers/staging/media/phy-rockchip-dphy-rx0/Documentation/devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml
> b/Documentation/devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml
> similarity index 100%
> rename from drivers/staging/media/phy-rockchip-dphy-rx0/Documentation/devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml
> rename to Documentation/devicetree/bindings/phy/rockchip-mipi-dphy-rx0.yaml
^ permalink raw reply
* [RFC PATCH] PCI: dwc: add support for Allwinner SoCs' PCIe controller
From: Icenowy Zheng @ 2020-04-02 16:05 UTC (permalink / raw)
To: Lorenzo Pieralisi, Andrew Murray, Bjorn Helgaas, Maxime Ripard,
Chen-Yu Tsai, Rob Herring
Cc: linux-kernel, linux-pci, linux-arm-kernel, devicetree,
linux-sunxi, Icenowy Zheng
The Allwinner H6 SoC uses DesignWare's PCIe controller to provide a PCIe
host.
However, on Allwinner H6, the PCIe host has bad MMIO, which needs to be
workarounded. A workaround with the EL2 hypervisor functionality of ARM
Cortex cores is now available, which wraps MMIO operations.
This patch is going to add a driver for the DWC PCIe controller
available in Allwinner SoCs, either the H6 one when wrapped by the
hypervisor (so that the driver can consider it as an ordinary PCIe
controller) or further not buggy ones.
Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
There's no device tree binding patch available, because I still have
questions on the device tree compatible string. I want to use it to
describe that this driver doesn't support the "native Allwinner H6 PCIe
controller", but a wrapped version with my hypervisor.
I think supporting a "para-physical" device is some new thing, so this
patch is RFC.
My hypervisor is at [1], and some basic usage documentation is at [2].
[1] https://github.com/Icenowy/aw-el2-barebone
[2] https://forum.armbian.com/topic/13529-a-try-on-utilizing-h6-pcie-with-virtualization/
drivers/pci/controller/dwc/Kconfig | 10 +
drivers/pci/controller/dwc/Makefile | 1 +
drivers/pci/controller/dwc/pcie-sunxi.c | 580 ++++++++++++++++++++++++
3 files changed, 591 insertions(+)
create mode 100644 drivers/pci/controller/dwc/pcie-sunxi.c
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 03dcaf65d159..23344e095668 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -247,6 +247,16 @@ config PCI_MESON
and therefore the driver re-uses the DesignWare core functions to
implement the driver.
+config PCIE_SUNXI
+ bool "Allwinner SoCs PCIe controllers"
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_DW_HOST
+ help
+ Say Y here it you want PCIe controller support on Allwinner SoCs.
+ Currently no PCIe controller is directly support by this driver,
+ although when wrapped with a hypervisor, Allwinner H6 PCIe controller
+ can be supported.
+
config PCIE_TEGRA194
tristate
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 8a637cfcf6e9..c1f14a47a4ad 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
obj-$(CONFIG_PCI_MESON) += pci-meson.o
obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
+obj-$(CONFIG_PCIE_SUNXI) += pcie-sunxi.o
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
# The following drivers are for devices that use the generic ACPI
diff --git a/drivers/pci/controller/dwc/pcie-sunxi.c b/drivers/pci/controller/dwc/pcie-sunxi.c
new file mode 100644
index 000000000000..555f910d8a03
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-sunxi.c
@@ -0,0 +1,580 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe RC driver for Allwinner DW PCIe controllers
+ *
+ * Copyright (C) 2018-2020 Icenowy Zheng <icenowy@aosc.io>
+ * Copyright (C) 2016 Allwinner Co., Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define PCIE_RC_LCR 0x7c
+#define USER_DEFINED_REGISTER_LIST 0x1000
+#define PCIE_LTSSM_ENABLE 0x1000
+#define PCIE_INT_PENDING 0x1018
+#define PCIE_ADDR_PAGE_CFG 0x1020
+#define PCIE_AWMISC_INF0_CTRL 0x1030
+#define PCIE_ARMISC_INF0_CTRL 0x1034
+#define PCIE_LINK_STATUS 0x143C
+#define PCIE_PHY_CFG 0x1800
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2
+#define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf
+#define SYS_CLK 0
+#define PAD_CLK 1
+#define RDLH_LINK_UP BIT(1)
+#define SMLH_LINK_UP BIT(0)
+#define PCIE_LINK_TRAINING BIT(0)
+#define PCIE_LINK_UP_MASK (0x3 << 16)
+#define LINK_WAIT_MAX_RETRIES 10
+#define LINK_WAIT_USLEEP_MIN 90000
+#define LINK_WAIT_USLEEP_MAX 100000
+
+#define PERST_DELAY_US 1000
+
+#define PCIE_BAR_NUM 6
+#define PCIE_MEM_FLAGS 0x4
+#define PCIE_IO_FLAGS 0x1
+#define PCIE_BAR_REG 0x4
+#define HIGH16_MASK GENMASK(31, 16)
+#define LOW16_MASK GENMASK(15, 0)
+
+#define to_sunxi_pcie(x) dev_get_drvdata((x)->dev)
+
+struct sunxi_pcie {
+ struct dw_pcie *pci;
+ int link_irq;
+ int msi_irq;
+ int speed_gen;
+ int io_voltage;
+ struct clk *pcie_ref;
+ struct clk *pcie_axi;
+ struct clk *pcie_aux;
+ struct clk *pcie_bus;
+ struct reset_control *pcie_power_rst;
+ struct reset_control *pcie_bus_rst;
+ struct regulator *reg_vcc;
+ struct regulator *reg_vdd;
+ struct regulator *reg_slot;
+ struct gpio_desc *perst_gpio;
+};
+
+static void sunxi_pcie_perst_gpio_assert(struct sunxi_pcie *pcie)
+{
+ gpiod_set_value_cansleep(pcie->perst_gpio, 1);
+ usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
+}
+
+static void sunxi_pcie_perst_gpio_deassert(struct sunxi_pcie *pcie)
+{
+ gpiod_set_value_cansleep(pcie->perst_gpio, 0);
+ usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
+}
+
+static void sunxi_pcie_clk_select(struct dw_pcie *pci, int status)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_PHY_CFG);
+ val &= ~(0x1 << 31);
+ val |= status << 31;
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CFG, val);
+}
+
+static void sunxi_pcie_ltssm_enable(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_LTSSM_ENABLE);
+ val |= PCIE_LINK_TRAINING;
+ dw_pcie_writel_dbi(pci, PCIE_LTSSM_ENABLE, val);
+}
+
+static void sunxi_pcie_irqpending(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_INT_PENDING);
+ val &= ~(0x3<<16);
+ dw_pcie_writel_dbi(pci, PCIE_INT_PENDING, val);
+}
+
+static void sunxi_pcie_phy_cfg(struct dw_pcie *pci, int enable)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_PHY_CFG);
+ if (enable)
+ val |= 0x1<<0;
+ else
+ val &= ~(0x1<<0);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CFG, val);
+}
+
+static void sunxi_pcie_irqmask(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_INT_PENDING);
+ val |= 0x3<<16;
+ dw_pcie_writel_dbi(pci, PCIE_INT_PENDING, val);
+}
+
+static void sunxi_pcie_ltssm_disable(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_LTSSM_ENABLE);
+ val &= ~PCIE_LINK_TRAINING;
+ dw_pcie_writel_dbi(pci, PCIE_LTSSM_ENABLE, val);
+}
+
+static int sunxi_pcie_wait_for_speed_change(struct dw_pcie *pci)
+{
+ u32 tmp;
+ unsigned int retries;
+
+ for (retries = 0; retries < 200; retries++) {
+ tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ /* Test if the speed change finished. */
+ if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
+ return 0;
+ usleep_range(100, 1000);
+ }
+
+ dev_err(pci->dev, "Speed change timeout\n");
+ return -EINVAL;
+}
+
+static int sunxi_pcie_link_up_status(struct dw_pcie *pci)
+{
+ u32 rc;
+ int ret;
+
+ rc = dw_pcie_readl_dbi(pci, PCIE_LINK_STATUS);
+ if ((rc & RDLH_LINK_UP) && (rc & SMLH_LINK_UP))
+ ret = 1;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int sunxi_pcie_speed_change(struct dw_pcie *pci, int gen)
+{
+ int val;
+ int ret;
+
+ if (gen == 2) {
+ val = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
+ val &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
+ val |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
+ dw_pcie_writel_dbi(pci, PCIE_RC_LCR, val);
+
+ /*
+ * Start Directed Speed Change so the best possible
+ * speed both link partners support can be negotiated.
+ */
+ val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val |= PORT_LOGIC_SPEED_CHANGE;
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+ ret = sunxi_pcie_wait_for_speed_change(pci);
+
+ if (!ret)
+ dev_info(pci->dev, "PCI-e speed of Gen%d\n", gen);
+ else
+ dev_info(pci->dev, "PCI-e speed of Gen1\n");
+ } else {
+ dev_info(pci->dev, "PCI-e speed of Gen1\n");
+ }
+
+ return 0;
+}
+
+static int sunxi_pcie_establish_link(struct dw_pcie *pci)
+{
+ struct sunxi_pcie *pcie = to_sunxi_pcie(pci);
+
+ if (dw_pcie_link_up(pci)) {
+ dev_err(pcie->pci->dev, "link is already up\n");
+ return 0;
+ }
+
+ sunxi_pcie_ltssm_enable(pci);
+ dw_pcie_wait_for_link(pci);
+
+ return 0;
+}
+
+static int sunxi_pcie_reset_deassert(struct sunxi_pcie *pcie)
+{
+ int ret;
+
+ ret = reset_control_deassert(pcie->pcie_bus_rst);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to deassert bus reset\n");
+ return ret;
+ }
+ usleep_range(1000, 2000);
+
+ if (pcie->io_voltage < 2000000)
+ sunxi_pcie_phy_cfg(pcie->pci, 1);
+ else
+ sunxi_pcie_phy_cfg(pcie->pci, 0);
+
+ usleep_range(1000, 2000);
+ ret = reset_control_deassert(pcie->pcie_power_rst);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to deassert power reset\n");
+ goto bus_reset_assert;
+ }
+
+ return 0;
+
+bus_reset_assert:
+ reset_control_assert(pcie->pcie_bus_rst);
+ return ret;
+}
+
+static int sunxi_pcie_clk_setup(struct sunxi_pcie *pcie)
+{
+ int ret;
+
+ ret = clk_prepare_enable(pcie->pcie_ref);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to enable pcie_ref clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(pcie->pcie_axi);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to enable pcie_axi clock\n");
+ goto disable_ref;
+ }
+
+ ret = clk_prepare_enable(pcie->pcie_aux);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to enable pcie_aux clock\n");
+ goto disable_axi;
+ }
+
+ ret = clk_prepare_enable(pcie->pcie_bus);
+ if (ret) {
+ dev_err(pcie->pci->dev, "unable to enable pcie_bus clock\n");
+ goto disable_aux;
+ }
+
+ return 0;
+
+disable_aux:
+ clk_disable_unprepare(pcie->pcie_aux);
+disable_axi:
+ clk_disable_unprepare(pcie->pcie_axi);
+disable_ref:
+ clk_disable_unprepare(pcie->pcie_ref);
+
+ return ret;
+}
+
+static int sunxi_pcie_regulator_enable(struct sunxi_pcie *pcie)
+{
+ int ret;
+
+ ret = regulator_enable(pcie->reg_vcc);
+ if (ret)
+ return ret;
+
+ pcie->io_voltage = regulator_get_voltage(pcie->reg_vcc);
+ if (pcie->io_voltage < 0) {
+ ret = pcie->io_voltage;
+ goto disable_vcc;
+ }
+
+ ret = regulator_enable(pcie->reg_vdd);
+ if (ret)
+ goto disable_vcc;
+
+ ret = regulator_enable(pcie->reg_slot);
+ if (ret)
+ goto disable_vdd;
+
+ return 0;
+
+disable_vdd:
+ regulator_disable(pcie->reg_vdd);
+disable_vcc:
+ regulator_disable(pcie->reg_vcc);
+
+ return ret;
+}
+
+static irqreturn_t sunxi_pcie_msi_irq_handler(int irq, void *arg)
+{
+ struct sunxi_pcie *pcie = (struct sunxi_pcie *)arg;
+
+ return dw_handle_msi_irq(&pcie->pci->pp);
+}
+
+static irqreturn_t sunxi_pcie_linkup_handler(int irq, void *arg)
+{
+ struct sunxi_pcie *pcie = (struct sunxi_pcie *)arg;
+
+ sunxi_pcie_irqpending(pcie->pci);
+
+ return IRQ_HANDLED;
+}
+
+static int sunxi_pcie_get_clk_rst(struct platform_device *pdev,
+ struct sunxi_pcie *pcie)
+{
+ struct device *dev = &pdev->dev;
+
+ pcie->pcie_ref = devm_clk_get(dev, "ref");
+ if (IS_ERR(pcie->pcie_ref)) {
+ dev_err(dev, "failed to get pcie ref clk\n");
+ return PTR_ERR(pcie->pcie_ref);
+ }
+
+ pcie->pcie_axi = devm_clk_get(dev, "axi");
+ if (IS_ERR(pcie->pcie_axi)) {
+ dev_err(dev, "failed to get pcie axi clk\n");
+ return PTR_ERR(pcie->pcie_axi);
+ }
+
+ pcie->pcie_aux = devm_clk_get(dev, "aux");
+ if (IS_ERR(pcie->pcie_aux)) {
+ dev_err(dev, "failed to get pcie aux clk\n");
+ return PTR_ERR(pcie->pcie_aux);
+ }
+
+ pcie->pcie_bus = devm_clk_get(dev, "bus");
+ if (IS_ERR(pcie->pcie_bus)) {
+ dev_err(dev, "failed to get pcie bus clk\n");
+ return PTR_ERR(pcie->pcie_bus);
+ }
+
+ pcie->pcie_bus_rst = devm_reset_control_get(dev, "bus");
+ if (IS_ERR(pcie->pcie_bus_rst)) {
+ dev_err(dev, "failed to get pcie bus reset\n");
+ return PTR_ERR(pcie->pcie_bus_rst);
+ }
+
+ pcie->pcie_power_rst = devm_reset_control_get(dev, "power");
+ if (IS_ERR(pcie->pcie_power_rst)) {
+ dev_err(dev, "failed to get pcie power reset\n");
+ return PTR_ERR(pcie->pcie_power_rst);
+ }
+
+ return 0;
+}
+
+static int sunxi_pcie_get_regulator(struct platform_device *pdev,
+ struct sunxi_pcie *pcie)
+{
+ struct device *dev = &pdev->dev;
+
+ pcie->reg_vcc = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(pcie->reg_vcc)) {
+ dev_err(dev, "failed to get pcie vcc supply\n");
+ return PTR_ERR(pcie->reg_vcc);
+ }
+
+ pcie->reg_vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(pcie->reg_vdd)) {
+ dev_err(dev, "failed to get pcie vdd supply\n");
+ return PTR_ERR(pcie->reg_vdd);
+ }
+
+ pcie->reg_slot = devm_regulator_get(dev, "slot");
+ if (IS_ERR(pcie->reg_slot)) {
+ dev_err(dev, "failed to get pcie slot supply\n");
+ return PTR_ERR(pcie->reg_slot);
+ }
+
+ return 0;
+}
+
+static int sunxi_pcie_host_init(struct pcie_port *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct sunxi_pcie *pcie = to_sunxi_pcie(pci);
+
+ sunxi_pcie_ltssm_disable(pci);
+ sunxi_pcie_clk_select(pci, PAD_CLK);
+
+ dw_pcie_setup_rc(pp);
+
+ sunxi_pcie_establish_link(pci);
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ dw_pcie_msi_init(pp);
+
+ sunxi_pcie_speed_change(pci, pcie->speed_gen);
+
+ return 0;
+}
+
+static const struct dw_pcie_host_ops sunxi_pcie_host_ops = {
+ .host_init = sunxi_pcie_host_init,
+};
+
+static const struct dw_pcie_ops sunxi_pcie_ops = {
+ .link_up = sunxi_pcie_link_up_status,
+};
+
+static int sunxi_add_pcie_port(struct sunxi_pcie *pcie,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct pcie_port *pp = &pcie->pci->pp;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq_byname(pdev, "msi");
+ if (pp->msi_irq < 0)
+ return pp->msi_irq;
+
+ ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+ sunxi_pcie_msi_irq_handler,
+ IRQF_SHARED, "sunxi-pcie-msi", pcie);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request MSI IRQ\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &sunxi_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_pcie_probe(struct platform_device *pdev)
+{
+ struct sunxi_pcie *sunxi_pcie;
+ struct dw_pcie *pci;
+ struct resource *dbi_res;
+ int ret;
+
+ sunxi_pcie = devm_kzalloc(&pdev->dev, sizeof(*sunxi_pcie),
+ GFP_KERNEL);
+ if (!sunxi_pcie)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(&pdev->dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ sunxi_pcie->pci = pci;
+
+ pci->dev = &pdev->dev;
+ pci->ops = &sunxi_pcie_ops;
+
+ platform_set_drvdata(pdev, sunxi_pcie);
+
+ dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+ if (!dbi_res)
+ return -ENODEV;
+
+ pci->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_res);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
+
+ ret = sunxi_pcie_get_clk_rst(pdev, sunxi_pcie);
+ if (ret)
+ return ret;
+
+ ret = sunxi_pcie_get_regulator(pdev, sunxi_pcie);
+ if (ret)
+ return ret;
+
+ sunxi_pcie->perst_gpio = devm_gpiod_get_optional(&pdev->dev, "perst", GPIOD_OUT_LOW);
+ if (IS_ERR(sunxi_pcie->perst_gpio))
+ return PTR_ERR(sunxi_pcie->perst_gpio);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "max-link-speed", &sunxi_pcie->speed_gen);
+ if (ret) {
+ dev_info(&pdev->dev, "No speed generation info specified, fallback to Gen1\n");
+ sunxi_pcie->speed_gen = 0x1;
+ }
+
+ ret = sunxi_pcie_regulator_enable(sunxi_pcie);
+ if (ret)
+ return ret;
+
+ ret = sunxi_pcie_clk_setup(sunxi_pcie);
+ if (ret)
+ return ret;
+
+ ret = sunxi_pcie_reset_deassert(sunxi_pcie);
+ if (ret)
+ return ret;
+
+ sunxi_pcie_perst_gpio_deassert(sunxi_pcie);
+
+ sunxi_pcie_irqmask(pci);
+ sunxi_pcie->link_irq = platform_get_irq_byname(pdev, "linkup");
+ ret = devm_request_irq(&pdev->dev, sunxi_pcie->link_irq,
+ sunxi_pcie_linkup_handler,
+ IRQF_SHARED, "sunxi-pcie-linkup", sunxi_pcie);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request linkup IRQ\n");
+ return ret;
+ }
+
+ ret = sunxi_add_pcie_port(sunxi_pcie, pdev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sunxi_pcie_remove(struct platform_device *pdev)
+{
+ struct sunxi_pcie *pcie = platform_get_drvdata(pdev);
+
+ sunxi_pcie_perst_gpio_assert(pcie);
+ reset_control_assert(pcie->pcie_bus_rst);
+ reset_control_assert(pcie->pcie_power_rst);
+ clk_disable_unprepare(pcie->pcie_ref);
+ clk_disable_unprepare(pcie->pcie_axi);
+ clk_disable_unprepare(pcie->pcie_aux);
+ clk_disable_unprepare(pcie->pcie_bus);
+
+ return 0;
+}
+
+static const struct of_device_id sunxi_pcie_of_match[] = {
+ { .compatible = "pine64,allwinner-h6-pcie-wrapped", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sunxi_pcie_of_match);
+
+static struct platform_driver sunxi_pcie_driver = {
+ .driver = {
+ .name = "sunxi-pcie",
+ .suppress_bind_attrs = true,
+ .of_match_table = sunxi_pcie_of_match,
+ },
+ .remove = sunxi_pcie_remove,
+ .probe = sunxi_pcie_probe,
+};
+builtin_platform_driver(sunxi_pcie_driver);
--
2.24.1
^ permalink raw reply related
* Re: [PATCH v7 04/10] lib/test_linear_ranges: add a test for the 'linear_ranges'
From: Vaittinen, Matti @ 2020-04-02 15:39 UTC (permalink / raw)
To: brendanhiggins@google.com
Cc: robh+dt@kernel.org, dan.j.williams@intel.com, tglx@linutronix.de,
talgi@mellanox.com, olteanv@gmail.com, davem@davemloft.net,
linux-pm@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, davidgow@google.com,
broonie@kernel.org, herbert@gondor.apana.org.au,
lgirdwood@gmail.com, rdunlap@infradead.org, mark.rutland@arm.com,
yamada.masahiro@socionext.com, Mutanen, Mikko, bp@suse.de,
mhiramat@kernel.org, krzk@kernel.org, mazziesaccount@gmail.com,
andy.shevchenko@gmail.com, andriy.shevchenko@linux.intel.com,
skhan@linuxfoundation.org, zaslonko@linux.ibm.com, Laine, Markus,
vincenzo.frascino@arm.com, sre@kernel.org,
linus.walleij@linaro.org, gregkh@linuxfoundation.org,
uwe@kleine-koenig.org, akpm@linux-foundation.org
In-Reply-To: <CAFd5g44gBrNti5Y_ctQKOE1_pWX3NAdTji1uH8m6dGj+tsJCew@mail.gmail.com>
On Wed, 2020-04-01 at 11:48 -0700, Brendan Higgins wrote:
> On Wed, Apr 1, 2020 at 1:45 AM Vaittinen, Matti
> <Matti.Vaittinen@fi.rohmeurope.com> wrote:
>
> Absolutely! Would you be interested in joining our mailing list:
>
> https://groups.google.com/g/kunit-dev
>
Sure :) How? The link gave me 404...
Best Regards,
Matti
^ permalink raw reply
* Re: [PATCH v4 9/9] arm64: dts: renesas: salvator: add a connection from adv748x codec (HDMI input) to the R-Car SoC
From: Alex Riesen @ 2020-04-02 15:35 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Kieran Bingham, Mauro Carvalho Chehab, Hans Verkuil,
Laurent Pinchart, Rob Herring, Mark Rutland, Kuninori Morimoto,
driverdevel, Linux Media Mailing List, Linux Kernel Mailing List,
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, Linux-Renesas
In-Reply-To: <CAMuHMdV6a=adKVmmRm_3qOSA37kDRfc63G+qQpN4UR-hj3R65g@mail.gmail.com>
Geert Uytterhoeven, Thu, Apr 02, 2020 17:26:15 +0200:
> On Thu, Apr 2, 2020 at 5:03 PM Alex Riesen <alexander.riesen@cetitec.com> wrote:
> > Geert Uytterhoeven, Mon, Mar 30, 2020 10:32:47 +0200:
> > > On Thu, Mar 26, 2020 at 11:55 AM Alex Riesen <alexander.riesen@cetitec.com> wrote:
> > > > --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi
> > > > +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
> > > > @@ -510,6 +511,15 @@ adv7482_txb: endpoint {
> > > > remote-endpoint = <&csi20_in>;
> > > > };
> > > > };
> > > > +
> > > > + port@c {
> > > > + reg = <12>;
> > > > +
> > > > + adv7482_i2s: endpoint {
> > > > + remote-endpoint = <&rsnd_endpoint3>;
> > > > + system-clock-direction-out;
> > > > + };
> > > > + };
> > >
> > > As the adv748x driver just ignores "invalid" endpoints...
> > >
> > > > @@ -758,8 +769,19 @@ &rcar_sound {
> > > > <&cpg CPG_MOD 1020>, <&cpg CPG_MOD 1021>,
> > > > <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>,
> > > > <&audio_clk_a>, <&cs2000>,
> > > > - <&audio_clk_c>,
> > > > + <&adv7482_hdmi_in>,
> > > > <&cpg CPG_CORE CPG_AUDIO_CLK_I>;
> > >
> > > ... and the rsnd driver ignores nonexistent-clocks, the DT change has no
> > > hard dependency on the driver change, and won't introduce regressions
> > > when included, right?
> >
> > Well, it maybe won't, but isn't it a little ... implicit?
> > And I'm no haste to include the changes, if you mean I can (or should) submit
> > the device tree patch separately.
>
> OK, fine for me to postpone (that'll be for v5.9, I guess?).
>
Looks scary :)
But yes, fine with me too.
v5 with ssi4 dummy in a moment.
Regards,
Alex
^ permalink raw reply
* Re: [PATCH v4 9/9] arm64: dts: renesas: salvator: add a connection from adv748x codec (HDMI input) to the R-Car SoC
From: Geert Uytterhoeven @ 2020-04-02 15:26 UTC (permalink / raw)
To: Alex Riesen, Geert Uytterhoeven, Kieran Bingham,
Mauro Carvalho Chehab, Hans Verkuil, Laurent Pinchart,
Rob Herring, Mark Rutland, Kuninori Morimoto, driverdevel,
Linux Media Mailing List, Linux Kernel Mailing List,
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, Linux-Renesas
In-Reply-To: <20200402141654.GB4291@pflmari>
Hi Alex,
On Thu, Apr 2, 2020 at 5:03 PM Alex Riesen <alexander.riesen@cetitec.com> wrote:
> Geert Uytterhoeven, Mon, Mar 30, 2020 10:32:47 +0200:
> > On Thu, Mar 26, 2020 at 11:55 AM Alex Riesen <alexander.riesen@cetitec.com> wrote:
> > > --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi
> > > +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
> > > @@ -510,6 +511,15 @@ adv7482_txb: endpoint {
> > > remote-endpoint = <&csi20_in>;
> > > };
> > > };
> > > +
> > > + port@c {
> > > + reg = <12>;
> > > +
> > > + adv7482_i2s: endpoint {
> > > + remote-endpoint = <&rsnd_endpoint3>;
> > > + system-clock-direction-out;
> > > + };
> > > + };
> >
> > As the adv748x driver just ignores "invalid" endpoints...
> >
> > > @@ -758,8 +769,19 @@ &rcar_sound {
> > > <&cpg CPG_MOD 1020>, <&cpg CPG_MOD 1021>,
> > > <&cpg CPG_MOD 1019>, <&cpg CPG_MOD 1018>,
> > > <&audio_clk_a>, <&cs2000>,
> > > - <&audio_clk_c>,
> > > + <&adv7482_hdmi_in>,
> > > <&cpg CPG_CORE CPG_AUDIO_CLK_I>;
> >
> > ... and the rsnd driver ignores nonexistent-clocks, the DT change has no
> > hard dependency on the driver change, and won't introduce regressions
> > when included, right?
>
> Well, it maybe won't, but isn't it a little ... implicit?
> And I'm no haste to include the changes, if you mean I can (or should) submit
> the device tree patch separately.
OK, fine for me to postpone (that'll be for v5.9, I guess?).
> > > @@ -777,6 +799,21 @@ rsnd_endpoint0: endpoint {
> > > capture = <&ssi1 &src1 &dvc1>;
> > > };
> > > };
> > > + rsnd_port3: port@3 {
> > > + reg = <3>;
> > > + rsnd_endpoint3: endpoint {
> > > + remote-endpoint = <&adv7482_i2s>;
> > > +
> > > + dai-tdm-slot-num = <8>;
> > > + dai-tdm-slot-width = <32>;
> > > + dai-format = "left_j";
> > > + mclk-fs = <256>;
> > > + bitclock-master = <&adv7482_i2s>;
> > > + frame-master = <&adv7482_i2s>;
> > > +
> > > + capture = <&ssi4>;
> > > + };
> > > + };
> > > };
> > > };
> >
> > However, as salvator-common.dtsi is shared by all Salvator-X(S) variants,
> > you'll have to add a dummy ssi4 node to r8a77961.dtsi first.
>
> I see. There are even two dummy SSI nodes already. I would prefer to submit
> the change together with other Salvator device tree changes. Is that alright?
Fine for me.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* RE: [PATCH v4 4/5] scsi: ufs-exynos: add UFS host support for Exynos SoCs
From: Alim Akhtar @ 2020-04-02 15:08 UTC (permalink / raw)
To: 'Avri Altman', robh+dt, devicetree, linux-scsi
Cc: krzk, martin.petersen, kwmad.kim, stanley.chu, cang,
linux-samsung-soc, linux-arm-kernel, linux-kernel
In-Reply-To: <SN6PR04MB4640B92BC9EA5CFEB74BE5EAFCCD0@SN6PR04MB4640.namprd04.prod.outlook.com>
Hi Avri
> -----Original Message-----
> From: Avri Altman <Avri.Altman@wdc.com>
> Sent: 28 March 2020 16:58
> To: Alim Akhtar <alim.akhtar@samsung.com>; robh+dt@kernel.org;
> devicetree@vger.kernel.org; linux-scsi@vger.kernel.org
> Cc: krzk@kernel.org; martin.petersen@oracle.com; kwmad.kim@samsung.com;
> stanley.chu@mediatek.com; cang@codeaurora.org; linux-samsung-
> soc@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
> kernel@vger.kernel.org
> Subject: RE: [PATCH v4 4/5] scsi: ufs-exynos: add UFS host support for Exynos
> SoCs
>
> Hi,
>
> > +
> > +long exynos_ufs_calc_time_cntr(struct exynos_ufs *ufs, long period) {
> > + const int precise = 10;
> > + long pclk_rate = ufs->pclk_rate;
> > + long clk_period, fraction;
> > +
> > + clk_period = UNIPRO_PCLK_PERIOD(ufs);
> > + fraction = ((NSEC_PER_SEC % pclk_rate) * precise) / pclk_rate;
> > +
> > + return (period * precise) / ((clk_period * precise) +
> > +fraction); }
> This helper essentially calculates a factor f, and returns period x f.
> Why not do that regardless of period?
>
The period can be different for different timing attributes, so this helper function takes the period and returns the timer counter value based on the pclk_rate.
> > +extern long exynos_ufs_calc_time_cntr(struct exynos_ufs *, long);
> Why this factor needed to be exported?
Yes, not needed, will correct this in next version, which I am planning to post soon.
Thanks for your time and review, let me know if you have more inputs.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox