diff options
author | Adrian Schmutzler <freifunk@adrianschmutzler.de> | 2020-08-19 15:41:30 +0200 |
---|---|---|
committer | Adrian Schmutzler <freifunk@adrianschmutzler.de> | 2020-08-20 14:19:39 +0200 |
commit | cf7c101135934710b1958092da9a0c9ad446de4b (patch) | |
tree | 8dee4a6f7c93e8bdd24fea0bb84d3c53044349db /target/linux/layerscape/patches-5.4 | |
parent | a14f5bb4bd263c21e103f13279d0c2ff03e48fe5 (diff) | |
download | upstream-cf7c101135934710b1958092da9a0c9ad446de4b.tar.gz upstream-cf7c101135934710b1958092da9a0c9ad446de4b.tar.bz2 upstream-cf7c101135934710b1958092da9a0c9ad446de4b.zip |
layerscape: remove useless pairs of kernel patches
The layerscape kernel patches appears to be just some uncleaned local
development tree, where patches are sometimes directly followed by
their revert. While this does not seem a problem in the first place,
it becomes incredibly unpleasant when the upstream kernel changes in
the relevant areas and requires rebase.
This removes all these patch-revert pairs and refreshs the rest.
It removes about 44000 lines of entirely useless code.
Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
Diffstat (limited to 'target/linux/layerscape/patches-5.4')
47 files changed, 24 insertions, 44576 deletions
diff --git a/target/linux/layerscape/patches-5.4/302-dts-0085-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interrupt.patch b/target/linux/layerscape/patches-5.4/302-dts-0085-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interrupt.patch deleted file mode 100644 index 17286a815e..0000000000 --- a/target/linux/layerscape/patches-5.4/302-dts-0085-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interrupt.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 3dc3a4c6ac9e8a0940a9974b8fe2da7641bfa3dd Mon Sep 17 00:00:00 2001 -From: Alex Marginean <alexandru.marginean@nxp.com> -Date: Thu, 22 Aug 2019 12:47:12 +0300 -Subject: [PATCH] arm64: dts: LS1028a-rdb: use Ethernet PHY interrupt - -Use the PHY interrupt wired to GPIO pins as part of MDIO WA performance -impact mitigation. - -Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> ---- - arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts | 10 ++++++++++ - 1 file changed, 10 insertions(+) - ---- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts -+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts -@@ -202,6 +202,8 @@ - #size-cells = <0>; - sgmii_phy0: ethernet-phy@2 { - reg = <0x2>; -+ interrupt-parent = <&gpio1>; -+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - }; - }; -@@ -213,18 +215,26 @@ - &enetc_mdio_pf3 { - qsgmii_phy1: ethernet-phy@4 { - reg = <0x10>; -+ interrupt-parent = <&gpio1>; -+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy2: ethernet-phy@5 { - reg = <0x11>; -+ interrupt-parent = <&gpio1>; -+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy3: ethernet-phy@6 { - reg = <0x12>; -+ interrupt-parent = <&gpio1>; -+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy4: ethernet-phy@7 { - reg = <0x13>; -+ interrupt-parent = <&gpio1>; -+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - }; - diff --git a/target/linux/layerscape/patches-5.4/302-dts-0092-arm64-dts-ls1028a-Add-DP-DT-nodes.patch b/target/linux/layerscape/patches-5.4/302-dts-0092-arm64-dts-ls1028a-Add-DP-DT-nodes.patch index a6d2253c73..a0fd27e350 100644 --- a/target/linux/layerscape/patches-5.4/302-dts-0092-arm64-dts-ls1028a-Add-DP-DT-nodes.patch +++ b/target/linux/layerscape/patches-5.4/302-dts-0092-arm64-dts-ls1028a-Add-DP-DT-nodes.patch @@ -32,7 +32,7 @@ Signed-off-by: Wen He <wen.he_1@nxp.com> --- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts +++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts -@@ -272,13 +272,6 @@ +@@ -262,13 +262,6 @@ }; &hdptx0 { diff --git a/target/linux/layerscape/patches-5.4/302-dts-0108-Revert-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interr.patch b/target/linux/layerscape/patches-5.4/302-dts-0108-Revert-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interr.patch deleted file mode 100644 index efb5cb6cb1..0000000000 --- a/target/linux/layerscape/patches-5.4/302-dts-0108-Revert-arm64-dts-LS1028a-rdb-use-Ethernet-PHY-interr.patch +++ /dev/null @@ -1,85 +0,0 @@ -From c57a0efc17627d0077408544ded02674aefdecae Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 16:59:02 +0200 -Subject: [PATCH] Revert "arm64: dts: LS1028a-rdb: use Ethernet PHY interrupt" - -This reverts commit 841edb98671cfc4d6f010393ac429c78082ec4bd. - -There are 2 separate issues with interrupts on the LS1028A-RDB board: - -1. The GPIO1_DAT25 interrupt line is shared, so there is a real risk of - race conditions if used in edge-triggered mode, as we currently do. - This can be illustrated in the following setup: - - Take 2 LS1028A-RDB boards - - Connect swp0 to swp0, swp1 to swp1, swp2 to swp2 - - Plug/unplug the power to board 2, 10 times in a row. This will make - the PHYs lose link simultaneously. - - Notice that at one point, the net devices on board 1 remain in a - state where not all the links are down (visible in "ip link"): - - 5: swp0: <BROADCAST,MULTICAST,UP> mtu 1468 qdisc pfifo_fast master br0 state UP mode DEFAULT group default qlen 1000 - link/ether be:97:36:d3:3d:70 brd ff:ff:ff:ff:ff:ff - 6: swp1: <BROADCAST,MULTICAST,UP> mtu 1468 qdisc pfifo_fast master br0 state UP mode DEFAULT group default qlen 1000 - link/ether be:97:36:d3:3d:71 brd ff:ff:ff:ff:ff:ff - 7: swp2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1468 qdisc pfifo_fast master br0 state DOWN mode DEFAULT group default qlen 1000 - link/ether be:97:36:d3:3d:72 brd ff:ff:ff:ff:ff:ff - - This cannot be solved by making the interrupts level-triggered, - because the gpio-mpc8xxx controller only supports generating - edge-triggered interrupts. So the effective reality is that we - cannot not use shared interrupts connected to the gpio1 - interrupt-parent. - -2. The uBUS1 and uBUS2 slots that share this interrupt line with the - Ethernet PHYs are not pulled up by default, they are left floating on - current revisions of the LS1028A-RDB boards. So sufficient electrical - noise on these lines will make the CPLD think there's an interrupt - request, so it asserts the GPIO1_DAT25 signal and leaves it asserted. - This means that the PHYs on those boards will never have link when - used in interrupt mode, because their IRQ will be masked by the uBUS - line that is erroneously kept asserted. In poll mode this issue does - not occur. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts | 10 ---------- - 1 file changed, 10 deletions(-) - ---- a/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts -+++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a-rdb.dts -@@ -206,8 +206,6 @@ - #size-cells = <0>; - sgmii_phy0: ethernet-phy@2 { - reg = <0x2>; -- interrupt-parent = <&gpio1>; -- interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - }; - }; -@@ -219,26 +217,18 @@ - &enetc_mdio_pf3 { - qsgmii_phy1: ethernet-phy@4 { - reg = <0x10>; -- interrupt-parent = <&gpio1>; -- interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy2: ethernet-phy@5 { - reg = <0x11>; -- interrupt-parent = <&gpio1>; -- interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy3: ethernet-phy@6 { - reg = <0x12>; -- interrupt-parent = <&gpio1>; -- interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - - qsgmii_phy4: ethernet-phy@7 { - reg = <0x13>; -- interrupt-parent = <&gpio1>; -- interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; - }; - diff --git a/target/linux/layerscape/patches-5.4/701-net-0275-mii-Add-helpers-for-parsing-SGMII-auto-negotiation.patch b/target/linux/layerscape/patches-5.4/701-net-0275-mii-Add-helpers-for-parsing-SGMII-auto-negotiation.patch deleted file mode 100644 index c783143dd7..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0275-mii-Add-helpers-for-parsing-SGMII-auto-negotiation.patch +++ /dev/null @@ -1,117 +0,0 @@ -From bc447c21304c1297f340d3daaf69915ebbc1f882 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 30 Sep 2019 19:20:26 +0300 -Subject: [PATCH] mii: Add helpers for parsing SGMII auto-negotiation - -Typically a MAC PCS auto-configures itself after it receives the -negotiated link settings from the PHY, but some MAC devices are more -special and need manual manipulation of the SGMII AN result. - -Therefore, add the bit definitions for the SGMII registers 4 and 5 -(local device ability, link partner ability), as well as a link_mode -conversion helper that can be used to feed the AN results into -phy_resolve_aneg_linkmode. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - include/linux/mii.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ - include/uapi/linux/mii.h | 10 ++++++++++ - 2 files changed, 60 insertions(+) - ---- a/include/linux/mii.h -+++ b/include/linux/mii.h -@@ -373,6 +373,56 @@ static inline u32 mii_lpa_to_ethtool_lpa - } - - /** -+ * mii_lpa_mod_linkmode_adv_sgmii -+ * @lp_advertising: pointer to destination link mode. -+ * @lpa: value of the MII_LPA register -+ * -+ * A small helper function that translates MII_LPA bits to -+ * linkmode advertisement settings for SGMII. -+ * Leaves other bits unchanged. -+ */ -+static inline void -+mii_lpa_mod_linkmode_lpa_sgmii(unsigned long *lp_advertising, u32 lpa) -+{ -+ u32 speed_duplex = lpa & LPA_SGMII_DPX_SPD_MASK; -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_1000HALF); -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_1000FULL); -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_100HALF); -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_100FULL); -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_10HALF); -+ -+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, lp_advertising, -+ speed_duplex == LPA_SGMII_10FULL); -+} -+ -+/** -+ * mii_lpa_to_linkmode_adv_sgmii -+ * @advertising: pointer to destination link mode. -+ * @lpa: value of the MII_LPA register -+ * -+ * A small helper function that translates MII_ADVERTISE bits -+ * to linkmode advertisement settings when in SGMII mode. -+ * Clears the old value of advertising. -+ */ -+static inline void mii_lpa_to_linkmode_lpa_sgmii(unsigned long *lp_advertising, -+ u32 lpa) -+{ -+ linkmode_zero(lp_advertising); -+ -+ mii_lpa_mod_linkmode_lpa_sgmii(lp_advertising, lpa); -+} -+ -+/** - * mii_adv_mod_linkmode_adv_t - * @advertising:pointer to destination link mode. - * @adv: value of the MII_ADVERTISE register ---- a/include/uapi/linux/mii.h -+++ b/include/uapi/linux/mii.h -@@ -71,6 +71,7 @@ - /* Advertisement control register. */ - #define ADVERTISE_SLCT 0x001f /* Selector bits */ - #define ADVERTISE_CSMA 0x0001 /* Only selector supported */ -+#define ADVERTISE_SGMII 0x0001 /* Can do SGMII */ - #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ - #define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ - #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -@@ -94,6 +95,7 @@ - - /* Link partner ability register. */ - #define LPA_SLCT 0x001f /* Same as advertise selector */ -+#define LPA_SGMII 0x0001 /* Can do SGMII */ - #define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ - #define LPA_1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ - #define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ -@@ -104,11 +106,19 @@ - #define LPA_1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/ - #define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ - #define LPA_PAUSE_CAP 0x0400 /* Can pause */ -+#define LPA_SGMII_DPX_SPD_MASK 0x1C00 /* SGMII duplex and speed bits */ -+#define LPA_SGMII_10HALF 0x0000 /* Can do SGMII 10mbps half-duplex */ -+#define LPA_SGMII_10FULL 0x1000 /* Can do SGMII 10mbps full-duplex */ -+#define LPA_SGMII_100HALF 0x0400 /* Can do SGMII 100mbps half-duplex */ -+#define LPA_SGMII_100FULL 0x1400 /* Can do SGMII 100mbps full-duplex */ - #define LPA_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ -+#define LPA_SGMII_1000HALF 0x0800 /* Can do SGMII 1000mbps half-duplex */ -+#define LPA_SGMII_1000FULL 0x1800 /* Can do SGMII 1000mbps full-duplex */ - #define LPA_RESV 0x1000 /* Unused... */ - #define LPA_RFAULT 0x2000 /* Link partner faulted */ - #define LPA_LPACK 0x4000 /* Link partner acked us */ - #define LPA_NPAGE 0x8000 /* Next page bit */ -+#define LPA_SGMII_LINK 0x8000 /* Link partner has link */ - - #define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) - #define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) diff --git a/target/linux/layerscape/patches-5.4/701-net-0276-net-phylink-make-QSGMII-a-valid-PHY-mode-for-in-band.patch b/target/linux/layerscape/patches-5.4/701-net-0276-net-phylink-make-QSGMII-a-valid-PHY-mode-for-in-band.patch deleted file mode 100644 index d04ed91139..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0276-net-phylink-make-QSGMII-a-valid-PHY-mode-for-in-band.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 4446e9789e662fe53dfcfe34551b5b74a013086c Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Fri, 22 Nov 2019 13:46:46 +0200 -Subject: [PATCH] net: phylink: make QSGMII a valid PHY mode for in-band AN - -QSGMII is just SGMII clocked at a higher frequency (5 Gbaud vs 1.25 -Gbaud). Logically it is just 4 SGMII interfaces multiplexed onto the -same physical lanes. Each MAC PCS has its own in-band AN process with -the system side of the QSGMII PHY, which is identical to the regular -SGMII AN process. So allow QSGMII as a valid in-band AN mode. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/phy/phylink.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -283,6 +283,7 @@ static int phylink_parse_mode(struct phy - - switch (pl->link_config.interface) { - case PHY_INTERFACE_MODE_SGMII: -+ case PHY_INTERFACE_MODE_QSGMII: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); - phylink_set(pl->supported, 100baseT_Half); diff --git a/target/linux/layerscape/patches-5.4/701-net-0277-net-phylink-call-mac_an_restart-for-SGMII-QSGMII-inb.patch b/target/linux/layerscape/patches-5.4/701-net-0277-net-phylink-call-mac_an_restart-for-SGMII-QSGMII-inb.patch deleted file mode 100644 index 487a520f32..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0277-net-phylink-call-mac_an_restart-for-SGMII-QSGMII-inb.patch +++ /dev/null @@ -1,29 +0,0 @@ -From d526228a8159d136c914c8f71d5944cda2f93d9a Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Thu, 28 Nov 2019 03:21:53 +0200 -Subject: [PATCH] net: phylink: call mac_an_restart for SGMII/QSGMII inband - interfaces too - -It doesn't quite make sense why restarting the AN process should be -unique to 802.3z (1000Base-X) modes. It is valid to put an SGMII PCS in -in-band AN mode, therefore also make PHYLINK re-trigger an -auto-negotiation if needed. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/phy/phylink.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -360,7 +360,9 @@ static void phylink_mac_config_up(struct - static void phylink_mac_an_restart(struct phylink *pl) - { - if (pl->link_config.an_enabled && -- phy_interface_mode_is_8023z(pl->link_config.interface)) -+ (phy_interface_mode_is_8023z(pl->link_config.interface) || -+ pl->link_config.interface == PHY_INTERFACE_MODE_SGMII || -+ pl->link_config.interface == PHY_INTERFACE_MODE_QSGMII)) - pl->ops->mac_an_restart(pl->config); - } - diff --git a/target/linux/layerscape/patches-5.4/701-net-0278-enetc-export-enetc_mdio-definitionns-to-include-linu.patch b/target/linux/layerscape/patches-5.4/701-net-0278-enetc-export-enetc_mdio-definitionns-to-include-linu.patch deleted file mode 100644 index b558a78cee..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0278-enetc-export-enetc_mdio-definitionns-to-include-linu.patch +++ /dev/null @@ -1,192 +0,0 @@ -From 210fde07cd9e3f2dc826f538ccec4e90b54eb7e5 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Fri, 22 Nov 2019 17:47:56 +0200 -Subject: [PATCH] enetc: export enetc_mdio definitionns to include/linux/fsl - -The Felix DSA switch has an internal MDIO bus that has the same register -map as the ENETC one, so the accessors can be reused. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/freescale/enetc/enetc_mdio.c | 18 +++++++++- - drivers/net/ethernet/freescale/enetc/enetc_mdio.h | 12 ------- - .../net/ethernet/freescale/enetc/enetc_pci_mdio.c | 41 +++++++++++++--------- - include/linux/fsl/enetc_mdio.h | 21 +++++++++++ - 4 files changed, 62 insertions(+), 30 deletions(-) - delete mode 100644 drivers/net/ethernet/freescale/enetc/enetc_mdio.h - create mode 100644 include/linux/fsl/enetc_mdio.h - ---- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -@@ -1,13 +1,13 @@ - // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) - /* Copyright 2019 NXP */ - -+#include <linux/fsl/enetc_mdio.h> - #include <linux/mdio.h> - #include <linux/of_mdio.h> - #include <linux/iopoll.h> - #include <linux/of.h> - - #include "enetc_pf.h" --#include "enetc_mdio.h" - - #define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */ - #define ENETC_MDIO_CTL 0x4 /* MDIO control */ -@@ -99,6 +99,7 @@ int enetc_mdio_write(struct mii_bus *bus - - return 0; - } -+EXPORT_SYMBOL_GPL(enetc_mdio_write); - - int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) - { -@@ -154,6 +155,21 @@ int enetc_mdio_read(struct mii_bus *bus, - - return value; - } -+EXPORT_SYMBOL_GPL(enetc_mdio_read); -+ -+struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs) -+{ -+ struct enetc_hw *hw; -+ -+ hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); -+ if (!hw) -+ return ERR_PTR(-ENOMEM); -+ -+ hw->port = port_regs; -+ -+ return hw; -+} -+EXPORT_SYMBOL_GPL(enetc_hw_alloc); - - int enetc_mdio_probe(struct enetc_pf *pf) - { ---- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.h -+++ /dev/null -@@ -1,12 +0,0 @@ --/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ --/* Copyright 2019 NXP */ -- --#include <linux/phy.h> -- --struct enetc_mdio_priv { -- struct enetc_hw *hw; -- int mdio_base; --}; -- --int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value); --int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum); ---- a/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c -@@ -1,8 +1,8 @@ - // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) - /* Copyright 2019 NXP */ -+#include <linux/fsl/enetc_mdio.h> - #include <linux/of_mdio.h> - #include "enetc_pf.h" --#include "enetc_mdio.h" - - #define ENETC_MDIO_DEV_ID 0xee01 - #define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO" -@@ -14,17 +14,29 @@ static int enetc_pci_mdio_probe(struct p - { - struct enetc_mdio_priv *mdio_priv; - struct device *dev = &pdev->dev; -+ void __iomem *port_regs; - struct enetc_hw *hw; - struct mii_bus *bus; - int err; - -- hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); -- if (!hw) -- return -ENOMEM; -+ port_regs = pci_iomap(pdev, 0, 0); -+ if (!port_regs) { -+ dev_err(dev, "iomap failed\n"); -+ err = -ENXIO; -+ goto err_ioremap; -+ } -+ -+ hw = enetc_hw_alloc(dev, port_regs); -+ if (IS_ERR(enetc_hw_alloc)) { -+ err = PTR_ERR(hw); -+ goto err_hw_alloc; -+ } - - bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); -- if (!bus) -- return -ENOMEM; -+ if (!bus) { -+ err = -ENOMEM; -+ goto err_mdiobus_alloc; -+ } - - bus->name = ENETC_MDIO_BUS_NAME; - bus->read = enetc_mdio_read; -@@ -39,7 +51,7 @@ static int enetc_pci_mdio_probe(struct p - err = pci_enable_device_mem(pdev); - if (err) { - dev_err(dev, "device enable failed\n"); -- return err; -+ goto err_pci_enable; - } - - err = pci_request_region(pdev, 0, KBUILD_MODNAME); -@@ -48,13 +60,6 @@ static int enetc_pci_mdio_probe(struct p - goto err_pci_mem_reg; - } - -- hw->port = pci_iomap(pdev, 0, 0); -- if (!hw->port) { -- err = -ENXIO; -- dev_err(dev, "iomap failed\n"); -- goto err_ioremap; -- } -- - err = of_mdiobus_register(bus, dev->of_node); - if (err) - goto err_mdiobus_reg; -@@ -64,12 +69,14 @@ static int enetc_pci_mdio_probe(struct p - return 0; - - err_mdiobus_reg: -- iounmap(mdio_priv->hw->port); --err_ioremap: - pci_release_mem_regions(pdev); - err_pci_mem_reg: - pci_disable_device(pdev); -- -+err_pci_enable: -+err_mdiobus_alloc: -+ iounmap(port_regs); -+err_hw_alloc: -+err_ioremap: - return err; - } - ---- /dev/null -+++ b/include/linux/fsl/enetc_mdio.h -@@ -0,0 +1,21 @@ -+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ -+/* Copyright 2019 NXP */ -+ -+#include <linux/phy.h> -+ -+/* PCS registers */ -+#define ENETC_PCS_LINK_TIMER1 0x12 -+#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0 -+#define ENETC_PCS_LINK_TIMER2 0x13 -+#define ENETC_PCS_LINK_TIMER2_VAL 0x0003 -+#define ENETC_PCS_IF_MODE 0x14 -+#define ENETC_PCS_IF_MODE_SGMII_AN 0x0003 -+ -+struct enetc_mdio_priv { -+ struct enetc_hw *hw; -+ int mdio_base; -+}; -+ -+int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value); -+int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum); -+struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs); diff --git a/target/linux/layerscape/patches-5.4/701-net-0279-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-value-of-.patch b/target/linux/layerscape/patches-5.4/701-net-0279-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-value-of-.patch deleted file mode 100644 index 02ead2712f..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0279-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-value-of-.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 5961ca6738507031a64f8580ceb8ab0b630d47a5 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Wed, 27 Nov 2019 19:21:13 +0200 -Subject: [PATCH] enetc: Set MDIO_CFG_HOLD to the recommended value of 2 - -This increases the MDIO hold time to 5 enet_clk cycles from the previous -value of 0. This is actually the out-of-reset value, that the driver was -previously overwriting with 0. Zero worked for the external MDIO, but -breaks communication with the internal MDIO buses on which the PCS of -ENETC SI's and Felix switch are found. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/freescale/enetc/enetc_mdio.c | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - ---- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -@@ -31,15 +31,19 @@ static inline void _enetc_mdio_wr(struct - _enetc_mdio_wr(mdio_priv, ENETC_##off, val) - #define enetc_mdio_rd_reg(off) enetc_mdio_rd(mdio_priv, off) - --#define ENETC_MDC_DIV 258 -- - #define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8) - #define MDIO_CFG_BSY BIT(0) - #define MDIO_CFG_RD_ER BIT(1) -+#define MDIO_CFG_HOLD(x) (((x) << 2) & GENMASK(4, 2)) - #define MDIO_CFG_ENC45 BIT(6) - /* external MDIO only - driven on neg MDC edge */ - #define MDIO_CFG_NEG BIT(23) - -+#define ENETC_EMDIO_CFG \ -+ (MDIO_CFG_HOLD(2) | \ -+ MDIO_CFG_CLKDIV(258) | \ -+ MDIO_CFG_NEG) -+ - #define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f) - #define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5) - #define MDIO_CTL_READ BIT(15) -@@ -61,7 +65,7 @@ int enetc_mdio_write(struct mii_bus *bus - u16 dev_addr; - int ret; - -- mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; -+ mdio_cfg = ENETC_EMDIO_CFG; - if (regnum & MII_ADDR_C45) { - dev_addr = (regnum >> 16) & 0x1f; - mdio_cfg |= MDIO_CFG_ENC45; -@@ -108,7 +112,7 @@ int enetc_mdio_read(struct mii_bus *bus, - u16 dev_addr, value; - int ret; - -- mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; -+ mdio_cfg = ENETC_EMDIO_CFG; - if (regnum & MII_ADDR_C45) { - dev_addr = (regnum >> 16) & 0x1f; - mdio_cfg |= MDIO_CFG_ENC45; diff --git a/target/linux/layerscape/patches-5.4/701-net-0280-net-mscc-ocelot-do-not-force-Felix-MACs-at-lower-spe.patch b/target/linux/layerscape/patches-5.4/701-net-0280-net-mscc-ocelot-do-not-force-Felix-MACs-at-lower-spe.patch deleted file mode 100644 index a31321fd17..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0280-net-mscc-ocelot-do-not-force-Felix-MACs-at-lower-spe.patch +++ /dev/null @@ -1,154 +0,0 @@ -From 4ba6e00c2f45bf4189ec6a8ef71b45346ae804f2 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Thu, 28 Nov 2019 15:36:10 +0200 -Subject: [PATCH] net: mscc: ocelot: do not force Felix MACs at lower speeds - than gigabit - -In the LS1028A, the VSC9959 switch was integrated with an NXP PCS which -performs SGMII AN and rate adaptation autonomously. The MAC does not -need to know about this, and forcing the MAC speed to something else, -when connected to a 10/100 link partner, actually breaks the GMII -internal link between the MAC and the PCS. - -Add a quirk system in the ocelot driver, and a first quirk called "PCS -performs rate adaptation", to distinguish the VSC7514 from the VSC9959 -regarding this behavior. - -Signed-off-by: Catalin Horghidan <catalin.horghidan@nxp.com> -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 1 + - drivers/net/dsa/ocelot/felix.h | 1 + - drivers/net/dsa/ocelot/felix_vsc9959.c | 1 + - drivers/net/ethernet/mscc/ocelot.c | 32 ++++++++++++++++++-------------- - include/soc/mscc/ocelot.h | 7 +++++++ - 5 files changed, 28 insertions(+), 14 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -249,6 +249,7 @@ static int felix_init_structs(struct fel - ocelot->num_stats = felix->info->num_stats; - ocelot->shared_queue_sz = felix->info->shared_queue_sz; - ocelot->ops = felix->info->ops; -+ ocelot->quirks = felix->info->quirks; - - base = pci_resource_start(felix->pdev, felix->info->pci_bar); - ---- a/drivers/net/dsa/ocelot/felix.h -+++ b/drivers/net/dsa/ocelot/felix.h -@@ -18,6 +18,7 @@ struct felix_info { - unsigned int num_stats; - int num_ports; - int pci_bar; -+ unsigned long quirks; - }; - - extern struct felix_info felix_info_vsc9959; ---- a/drivers/net/dsa/ocelot/felix_vsc9959.c -+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c -@@ -584,4 +584,5 @@ struct felix_info felix_info_vsc9959 = { - .shared_queue_sz = 128 * 1024, - .num_ports = 6, - .pci_bar = 4, -+ .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION, - }; ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -409,27 +409,32 @@ static u16 ocelot_wm_enc(u16 value) - void ocelot_adjust_link(struct ocelot *ocelot, int port, - struct phy_device *phydev) - { -+ int speed, mac_speed, mac_mode = DEV_MAC_MODE_CFG_FDX_ENA; - struct ocelot_port *ocelot_port = ocelot->ports[port]; -- int speed, mode = 0; - -- switch (phydev->speed) { -+ if (ocelot->quirks & OCELOT_PCS_PERFORMS_RATE_ADAPTATION) -+ speed = SPEED_1000; -+ else -+ speed = phydev->speed; -+ -+ switch (speed) { - case SPEED_10: -- speed = OCELOT_SPEED_10; -+ mac_speed = OCELOT_SPEED_10; - break; - case SPEED_100: -- speed = OCELOT_SPEED_100; -+ mac_speed = OCELOT_SPEED_100; - break; - case SPEED_1000: -- speed = OCELOT_SPEED_1000; -- mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; -+ mac_speed = OCELOT_SPEED_1000; -+ mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - case SPEED_2500: -- speed = OCELOT_SPEED_2500; -- mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; -+ mac_speed = OCELOT_SPEED_2500; -+ mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - default: - dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n", -- port, phydev->speed); -+ port, speed); - return; - } - -@@ -439,8 +444,7 @@ void ocelot_adjust_link(struct ocelot *o - return; - - /* Only full duplex supported for now */ -- ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA | -- mode, DEV_MAC_MODE_CFG); -+ ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); - - if (ocelot->ops->pcs_init) - ocelot->ops->pcs_init(ocelot, port); -@@ -451,11 +455,11 @@ void ocelot_adjust_link(struct ocelot *o - - /* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of - * reset */ -- ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed), -+ ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(mac_speed), - DEV_CLOCK_CFG); - - /* No PFC */ -- ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed), -+ ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(mac_speed), - ANA_PFC_PFC_CFG, port); - - /* Core: Enable port for frame transfer */ -@@ -469,7 +473,7 @@ void ocelot_adjust_link(struct ocelot *o - SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA | - SYS_MAC_FC_CFG_ZERO_PAUSE_ENA | - SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -- SYS_MAC_FC_CFG_FC_LINK_SPEED(speed), -+ SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed), - SYS_MAC_FC_CFG, port); - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); - } ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -404,6 +404,11 @@ enum ocelot_tag_prefix { - OCELOT_TAG_PREFIX_LONG, - }; - -+/* Hardware quirks (differences between switch instantiations) */ -+enum { -+ OCELOT_PCS_PERFORMS_RATE_ADAPTATION = BIT(0), -+}; -+ - struct ocelot; - - struct ocelot_ops { -@@ -464,6 +469,8 @@ struct ocelot { - struct delayed_work stats_work; - struct workqueue_struct *stats_queue; - -+ unsigned long quirks; -+ - u8 ptp:1; - struct ptp_clock *ptp_clock; - struct ptp_clock_info ptp_info; diff --git a/target/linux/layerscape/patches-5.4/701-net-0281-net-mscc-ocelot-convert-to-PHYLINK.patch b/target/linux/layerscape/patches-5.4/701-net-0281-net-mscc-ocelot-convert-to-PHYLINK.patch deleted file mode 100644 index fcf28b8667..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0281-net-mscc-ocelot-convert-to-PHYLINK.patch +++ /dev/null @@ -1,689 +0,0 @@ -From 0a2b7489bf60d24a54e16147b416f339ebe4f511 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 18 Nov 2019 18:05:01 +0200 -Subject: [PATCH] net: mscc: ocelot: convert to PHYLINK - -This patch reworks ocelot_board.c (aka the MIPS on the VSC7514) to -register a PHYLINK instance for each port. The registration code is -local to the VSC7514, but the PHYLINK callback implementation is common -so that the Felix DSA front-end can use it as well (but DSA does its own -registration). - -Now Felix can use native PHYLINK callbacks instead of the PHYLIB -adaptation layer in DSA, which had issues supporting fixed-link slave -ports (no struct phy_device to pass to the adjust_link callback), as -well as fixed-link CPU port at 2.5Gbps. - -The old code from ocelot_port_enable and ocelot_port_disable has been -moved into ocelot_phylink_mac_link_up and ocelot_phylink_mac_link_down. - -The PHY connect operation has been moved from ocelot_port_open to -mscc_ocelot_probe in ocelot_board.c. - -The phy_set_mode_ext() call for the SerDes PHY has also been moved into -mscc_ocelot_probe from ocelot_port_open, and since that was the only -reason why a reference to it was kept in ocelot_port_private, that -reference was removed. - -Again, the usage of phy_interface_t phy_mode is now local to -mscc_ocelot_probe only, after moving the PHY connect operation. -So it was also removed from ocelot_port_private. -*Maybe* in the future, it can be added back to the common struct -ocelot_port, with the purpose of validating mismatches between -state->phy_interface and ocelot_port->phy_mode in PHYLINK callbacks. -But at the moment that is not critical, since other DSA drivers are not -doing that either. No SFP+ modules are in use with Felix/Ocelot yet, to -my knowledge. - -In-band AN is not yet supported, due to the fact that this is a mostly -mechanical patch for the moment. The mac_an_restart PHYLINK operation -needs to be implemented, as well as mac_link_state. Both are SerDes -specific, and Felix does not have its PCS configured yet (it works just -by virtue of U-Boot initialization at the moment). - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 65 +++++++++---- - drivers/net/ethernet/mscc/Kconfig | 2 +- - drivers/net/ethernet/mscc/ocelot.c | 150 ++++++++++++++++-------------- - drivers/net/ethernet/mscc/ocelot.h | 13 +-- - drivers/net/ethernet/mscc/ocelot_board.c | 154 +++++++++++++++++++++++++++---- - include/soc/mscc/ocelot.h | 22 ++++- - 6 files changed, 289 insertions(+), 117 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -58,14 +58,6 @@ static int felix_set_ageing_time(struct - return 0; - } - --static void felix_adjust_link(struct dsa_switch *ds, int port, -- struct phy_device *phydev) --{ -- struct ocelot *ocelot = ds->priv; -- -- ocelot_adjust_link(ocelot, port, phydev); --} -- - static int felix_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) - { -@@ -185,21 +177,59 @@ static int felix_tsn_enable(struct dsa_p - } - #endif - --static int felix_port_enable(struct dsa_switch *ds, int port, -- struct phy_device *phy) -+static void felix_phylink_validate(struct dsa_switch *ds, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state) - { - struct ocelot *ocelot = ds->priv; - -- ocelot_port_enable(ocelot, port, phy); -+ ocelot_phylink_validate(ocelot, port, supported, state); -+} -+ -+static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port, -+ struct phylink_link_state *state) -+{ -+ struct ocelot *ocelot = ds->priv; -+ -+ ocelot_phylink_mac_pcs_get_state(ocelot, port, state); - - return 0; - } - --static void felix_port_disable(struct dsa_switch *ds, int port) -+static void felix_phylink_mac_config(struct dsa_switch *ds, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) -+{ -+ struct ocelot *ocelot = ds->priv; -+ -+ ocelot_phylink_mac_config(ocelot, port, link_an_mode, state); -+} -+ -+static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port) -+{ -+ struct ocelot *ocelot = ds->priv; -+ -+ ocelot_phylink_mac_an_restart(ocelot, port); -+} -+ -+static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface) -+{ -+ struct ocelot *ocelot = ds->priv; -+ -+ ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface); -+} -+ -+static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface, -+ struct phy_device *phydev) - { - struct ocelot *ocelot = ds->priv; - -- return ocelot_port_disable(ocelot, port); -+ ocelot_phylink_mac_link_up(ocelot, port, link_an_mode, interface, -+ phydev); - } - - static void felix_get_strings(struct dsa_switch *ds, int port, -@@ -417,9 +447,12 @@ static const struct dsa_switch_ops felix - .get_ethtool_stats = felix_get_ethtool_stats, - .get_sset_count = felix_get_sset_count, - .get_ts_info = felix_get_ts_info, -- .adjust_link = felix_adjust_link, -- .port_enable = felix_port_enable, -- .port_disable = felix_port_disable, -+ .phylink_validate = felix_phylink_validate, -+ .phylink_mac_link_state = felix_phylink_mac_pcs_get_state, -+ .phylink_mac_config = felix_phylink_mac_config, -+ .phylink_mac_an_restart = felix_phylink_mac_an_restart, -+ .phylink_mac_link_down = felix_phylink_mac_link_down, -+ .phylink_mac_link_up = felix_phylink_mac_link_up, - .port_fdb_dump = felix_fdb_dump, - .port_fdb_add = felix_fdb_add, - .port_fdb_del = felix_fdb_del, ---- a/drivers/net/ethernet/mscc/Kconfig -+++ b/drivers/net/ethernet/mscc/Kconfig -@@ -15,7 +15,7 @@ config MSCC_OCELOT_SWITCH - tristate "Ocelot switch driver" - depends on NET_SWITCHDEV - depends on HAS_IOMEM -- select PHYLIB -+ select PHYLINK - select REGMAP_MMIO - help - This driver supports the Ocelot network switch device. ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -13,7 +13,7 @@ - #include <linux/kernel.h> - #include <linux/module.h> - #include <linux/netdevice.h> --#include <linux/phy.h> -+#include <linux/phylink.h> - #include <linux/ptp_clock_kernel.h> - #include <linux/skbuff.h> - #include <linux/iopoll.h> -@@ -406,18 +406,66 @@ static u16 ocelot_wm_enc(u16 value) - return value; - } - --void ocelot_adjust_link(struct ocelot *ocelot, int port, -- struct phy_device *phydev) -+void ocelot_phylink_validate(struct ocelot *ocelot, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state) -+{ -+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -+ -+ if (state->interface != PHY_INTERFACE_MODE_NA && -+ state->interface != PHY_INTERFACE_MODE_GMII && -+ state->interface != PHY_INTERFACE_MODE_SGMII && -+ state->interface != PHY_INTERFACE_MODE_QSGMII) { -+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -+ return; -+ } -+ -+ /* No half-duplex. */ -+ phylink_set_port_modes(mask); -+ phylink_set(mask, Autoneg); -+ phylink_set(mask, Pause); -+ phylink_set(mask, Asym_Pause); -+ phylink_set(mask, 10baseT_Full); -+ phylink_set(mask, 100baseT_Full); -+ phylink_set(mask, 1000baseT_Full); -+ phylink_set(mask, 2500baseT_Full); -+ -+ bitmap_and(supported, supported, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+ bitmap_and(state->advertising, state->advertising, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+EXPORT_SYMBOL(ocelot_phylink_validate); -+ -+void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, -+ struct phylink_link_state *state) -+{ -+ state->link = 1; -+} -+EXPORT_SYMBOL(ocelot_phylink_mac_pcs_get_state); -+ -+void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port) -+{ -+ /* Not supported */ -+} -+EXPORT_SYMBOL(ocelot_phylink_mac_an_restart); -+ -+void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) - { - int speed, mac_speed, mac_mode = DEV_MAC_MODE_CFG_FDX_ENA; - struct ocelot_port *ocelot_port = ocelot->ports[port]; -+ u32 mac_fc_cfg; - - if (ocelot->quirks & OCELOT_PCS_PERFORMS_RATE_ADAPTATION) - speed = SPEED_1000; - else -- speed = phydev->speed; -+ speed = state->speed; - - switch (speed) { -+ case SPEED_UNKNOWN: -+ return; - case SPEED_10: - mac_speed = OCELOT_SPEED_10; - break; -@@ -433,16 +481,11 @@ void ocelot_adjust_link(struct ocelot *o - mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - default: -- dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n", -+ dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n", - port, speed); - return; - } - -- phy_print_status(phydev); -- -- if (!phydev->link) -- return; -- - /* Only full duplex supported for now */ - ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); - -@@ -469,27 +512,36 @@ void ocelot_adjust_link(struct ocelot *o - QSYS_SWITCH_PORT_MODE, port); - - /* Flow control */ -- ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | -- SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA | -- SYS_MAC_FC_CFG_ZERO_PAUSE_ENA | -- SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -- SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed), -- SYS_MAC_FC_CFG, port); -+ mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed); -+ if (state->pause & MLO_PAUSE_RX) -+ mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; -+ if (state->pause & MLO_PAUSE_TX) -+ mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | -+ SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | -+ SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -+ SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; -+ ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); -+ - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); - } --EXPORT_SYMBOL(ocelot_adjust_link); -+EXPORT_SYMBOL(ocelot_phylink_mac_config); - --static void ocelot_port_adjust_link(struct net_device *dev) -+void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface) - { -- struct ocelot_port_private *priv = netdev_priv(dev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -+ struct ocelot_port *ocelot_port = ocelot->ports[port]; - -- ocelot_adjust_link(ocelot, port, dev->phydev); -+ ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); -+ ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, -+ QSYS_SWITCH_PORT_MODE, port); - } -+EXPORT_SYMBOL(ocelot_phylink_mac_link_down); - --void ocelot_port_enable(struct ocelot *ocelot, int port, -- struct phy_device *phy) -+void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface, -+ struct phy_device *phy) - { - /* Enable receiving frames on the port, and activate auto-learning of - * MAC addresses. -@@ -499,62 +551,22 @@ void ocelot_port_enable(struct ocelot *o - ANA_PORT_PORT_CFG_PORTID_VAL(port), - ANA_PORT_PORT_CFG, port); - } --EXPORT_SYMBOL(ocelot_port_enable); -+EXPORT_SYMBOL(ocelot_phylink_mac_link_up); - - static int ocelot_port_open(struct net_device *dev) - { - struct ocelot_port_private *priv = netdev_priv(dev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- int err; -- -- if (priv->serdes) { -- err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, -- priv->phy_mode); -- if (err) { -- netdev_err(dev, "Could not set mode of SerDes\n"); -- return err; -- } -- } -- -- err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, -- priv->phy_mode); -- if (err) { -- netdev_err(dev, "Could not attach to PHY\n"); -- return err; -- } - -- dev->phydev = priv->phy; -- -- phy_attached_info(priv->phy); -- phy_start(priv->phy); -- -- ocelot_port_enable(ocelot, port, priv->phy); -+ phylink_start(priv->phylink); - - return 0; - } - --void ocelot_port_disable(struct ocelot *ocelot, int port) --{ -- struct ocelot_port *ocelot_port = ocelot->ports[port]; -- -- ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); -- ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, -- QSYS_SWITCH_PORT_MODE, port); --} --EXPORT_SYMBOL(ocelot_port_disable); -- - static int ocelot_port_stop(struct net_device *dev) - { - struct ocelot_port_private *priv = netdev_priv(dev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- phy_disconnect(priv->phy); -- -- dev->phydev = NULL; - -- ocelot_port_disable(ocelot, port); -+ phylink_stop(priv->phylink); - - return 0; - } -@@ -2251,8 +2263,7 @@ void ocelot_init_port(struct ocelot *oce - EXPORT_SYMBOL(ocelot_init_port); - - int ocelot_probe_port(struct ocelot *ocelot, u8 port, -- void __iomem *regs, -- struct phy_device *phy) -+ void __iomem *regs) - { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; -@@ -2265,7 +2276,6 @@ int ocelot_probe_port(struct ocelot *oce - SET_NETDEV_DEV(dev, ocelot->dev); - priv = netdev_priv(dev); - priv->dev = dev; -- priv->phy = phy; - priv->chip_port = port; - ocelot_port = &priv->port; - ocelot_port->ocelot = ocelot; ---- a/drivers/net/ethernet/mscc/ocelot.h -+++ b/drivers/net/ethernet/mscc/ocelot.h -@@ -12,8 +12,7 @@ - #include <linux/etherdevice.h> - #include <linux/if_vlan.h> - #include <linux/net_tstamp.h> --#include <linux/phy.h> --#include <linux/phy/phy.h> -+#include <linux/phylink.h> - #include <linux/platform_device.h> - #include <linux/ptp_clock_kernel.h> - #include <linux/regmap.h> -@@ -65,14 +64,12 @@ struct ocelot_multicast { - struct ocelot_port_private { - struct ocelot_port port; - struct net_device *dev; -- struct phy_device *phy; -+ struct phylink *phylink; -+ struct phylink_config phylink_config; - u8 chip_port; - - u8 vlan_aware; - -- phy_interface_t phy_mode; -- struct phy *serdes; -- - struct ocelot_port_tc tc; - }; - -@@ -83,9 +80,7 @@ void ocelot_port_writel(struct ocelot_po - #define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val)) - - int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops); --int ocelot_probe_port(struct ocelot *ocelot, u8 port, -- void __iomem *regs, -- struct phy_device *phy); -+int ocelot_probe_port(struct ocelot *ocelot, u8 port, void __iomem *regs); - - void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, - enum ocelot_tag_prefix injection, ---- a/drivers/net/ethernet/mscc/ocelot_board.c -+++ b/drivers/net/ethernet/mscc/ocelot_board.c -@@ -13,6 +13,7 @@ - #include <linux/mfd/syscon.h> - #include <linux/skbuff.h> - #include <net/switchdev.h> -+#include <linux/phy/phy.h> - - #include "ocelot.h" - -@@ -262,6 +263,91 @@ static const struct ocelot_ops ocelot_op - .reset = ocelot_reset, - }; - -+static void ocelot_port_phylink_validate(struct phylink_config *config, -+ unsigned long *supported, -+ struct phylink_link_state *state) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ ocelot_phylink_validate(ocelot, port, supported, state); -+} -+ -+static int -+ocelot_port_phylink_mac_pcs_get_state(struct phylink_config *config, -+ struct phylink_link_state *state) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ ocelot_phylink_mac_pcs_get_state(ocelot, port, state); -+ -+ return 0; -+} -+ -+static void ocelot_port_phylink_mac_an_restart(struct phylink_config *config) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ ocelot_phylink_mac_an_restart(ocelot, port); -+} -+ -+static void -+ocelot_port_phylink_mac_config(struct phylink_config *config, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ ocelot_phylink_mac_config(ocelot, port, link_an_mode, state); -+} -+ -+static void ocelot_port_phylink_mac_link_down(struct phylink_config *config, -+ unsigned int link_an_mode, -+ phy_interface_t interface) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ return ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, -+ interface); -+} -+ -+static void ocelot_port_phylink_mac_link_up(struct phylink_config *config, -+ unsigned int link_an_mode, -+ phy_interface_t interface, -+ struct phy_device *phy) -+{ -+ struct net_device *ndev = to_net_dev(config->dev); -+ struct ocelot_port_private *priv = netdev_priv(ndev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ return ocelot_phylink_mac_link_up(ocelot, port, link_an_mode, -+ interface, phy); -+} -+ -+static const struct phylink_mac_ops ocelot_phylink_ops = { -+ .validate = ocelot_port_phylink_validate, -+ .mac_link_state = ocelot_port_phylink_mac_pcs_get_state, -+ .mac_an_restart = ocelot_port_phylink_mac_an_restart, -+ .mac_config = ocelot_port_phylink_mac_config, -+ .mac_link_down = ocelot_port_phylink_mac_link_down, -+ .mac_link_up = ocelot_port_phylink_mac_link_up, -+}; -+ - static int mscc_ocelot_probe(struct platform_device *pdev) - { - struct device_node *np = pdev->dev.of_node; -@@ -369,8 +455,6 @@ static int mscc_ocelot_probe(struct plat - for_each_available_child_of_node(ports, portnp) { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; -- struct device_node *phy_node; -- struct phy_device *phy; - struct resource *res; - struct phy *serdes; - void __iomem *regs; -@@ -389,16 +473,7 @@ static int mscc_ocelot_probe(struct plat - if (IS_ERR(regs)) - continue; - -- phy_node = of_parse_phandle(portnp, "phy-handle", 0); -- if (!phy_node) -- continue; -- -- phy = of_phy_find_device(phy_node); -- of_node_put(phy_node); -- if (!phy) -- continue; -- -- err = ocelot_probe_port(ocelot, port, regs, phy); -+ err = ocelot_probe_port(ocelot, port, regs); - if (err) { - of_node_put(portnp); - goto out_put_ports; -@@ -412,9 +487,7 @@ static int mscc_ocelot_probe(struct plat - if (phy_mode < 0) - phy_mode = PHY_INTERFACE_MODE_NA; - -- priv->phy_mode = phy_mode; -- -- switch (priv->phy_mode) { -+ switch (phy_mode) { - case PHY_INTERFACE_MODE_NA: - continue; - case PHY_INTERFACE_MODE_SGMII: -@@ -451,7 +524,41 @@ static int mscc_ocelot_probe(struct plat - goto out_put_ports; - } - -- priv->serdes = serdes; -+ if (serdes) { -+ err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, -+ phy_mode); -+ if (err) { -+ dev_err(ocelot->dev, -+ "Could not set mode of SerDes\n"); -+ of_node_put(portnp); -+ goto out_put_ports; -+ } -+ } -+ -+ priv->phylink_config.dev = &priv->dev->dev; -+ priv->phylink_config.type = PHYLINK_NETDEV; -+ -+ priv->phylink = phylink_create(&priv->phylink_config, -+ of_fwnode_handle(portnp), -+ phy_mode, &ocelot_phylink_ops); -+ if (IS_ERR(priv->phylink)) { -+ dev_err(ocelot->dev, -+ "Could not create a phylink instance (%ld)\n", -+ PTR_ERR(priv->phylink)); -+ err = PTR_ERR(priv->phylink); -+ priv->phylink = NULL; -+ of_node_put(portnp); -+ goto out_put_ports; -+ } -+ -+ err = phylink_of_phy_connect(priv->phylink, portnp, 0); -+ if (err) { -+ dev_err(ocelot->dev, "Could not connect to PHY: %d\n", -+ err); -+ phylink_destroy(priv->phylink); -+ of_node_put(portnp); -+ goto out_put_ports; -+ } - } - - register_netdevice_notifier(&ocelot_netdevice_nb); -@@ -468,12 +575,27 @@ out_put_ports: - static int mscc_ocelot_remove(struct platform_device *pdev) - { - struct ocelot *ocelot = platform_get_drvdata(pdev); -+ int port; - - ocelot_deinit(ocelot); - unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); - unregister_switchdev_notifier(&ocelot_switchdev_nb); - unregister_netdevice_notifier(&ocelot_netdevice_nb); - -+ for (port = 0; port < ocelot->num_phys_ports; port++) { -+ struct ocelot_port_private *priv; -+ -+ priv = container_of(ocelot->ports[port], -+ struct ocelot_port_private, -+ port); -+ -+ if (priv->phylink) { -+ rtnl_lock(); -+ phylink_destroy(priv->phylink); -+ rtnl_unlock(); -+ } -+ } -+ - return 0; - } - ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -518,17 +518,12 @@ void ocelot_deinit(struct ocelot *ocelot - void ocelot_init_port(struct ocelot *ocelot, int port); - - /* DSA callbacks */ --void ocelot_port_enable(struct ocelot *ocelot, int port, -- struct phy_device *phy); --void ocelot_port_disable(struct ocelot *ocelot, int port); - void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); - void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data); - int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset); - int ocelot_get_ts_info(struct ocelot *ocelot, int port, - struct ethtool_ts_info *info); - void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs); --void ocelot_adjust_link(struct ocelot *ocelot, int port, -- struct phy_device *phydev); - void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, - bool vlan_aware); - void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state); -@@ -597,4 +592,21 @@ int ocelot_rtag_parse_enable(struct ocel - int ocelot_dscp_set(struct ocelot *ocelot, int port, - bool enable, const u8 dscp_ix, - struct tsn_qos_switch_dscp_conf *c); -+void ocelot_phylink_validate(struct ocelot *ocelot, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state); -+void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, -+ struct phylink_link_state *state); -+void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port); -+void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state); -+void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface); -+void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ phy_interface_t interface, -+ struct phy_device *phy); -+ - #endif diff --git a/target/linux/layerscape/patches-5.4/701-net-0282-net-mscc-ocelot-introduce-more-focused-PCS-ops-for-P.patch b/target/linux/layerscape/patches-5.4/701-net-0282-net-mscc-ocelot-introduce-more-focused-PCS-ops-for-P.patch deleted file mode 100644 index d7ae1196dd..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0282-net-mscc-ocelot-introduce-more-focused-PCS-ops-for-P.patch +++ /dev/null @@ -1,169 +0,0 @@ -From 07f63e91f5e81f7f36c1e646f72c394c7f60c05c Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Fri, 22 Nov 2019 13:46:34 +0200 -Subject: [PATCH] net: mscc: ocelot: introduce more focused PCS ops for PHYLINK - -The reason for doing this is that the 2 mainline Ocelot switches so far, -VSC7514 and VSC9959, have radically different SoC/SerDes integration. So -although the PHYLINK callbacks are common, the implementations will -actually lie in device-specific function pointers. - -Also, there was a duplicated and unused function pointer for pcs_init in -struct ocelot, remove that. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/mscc/ocelot.c | 36 ++++++++------------------------ - drivers/net/ethernet/mscc/ocelot_board.c | 35 ++++++++++++++++++++++++++++++- - include/soc/mscc/ocelot.h | 12 ++++++++--- - 3 files changed, 52 insertions(+), 31 deletions(-) - ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -410,43 +410,25 @@ void ocelot_phylink_validate(struct ocel - unsigned long *supported, - struct phylink_link_state *state) - { -- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -- -- if (state->interface != PHY_INTERFACE_MODE_NA && -- state->interface != PHY_INTERFACE_MODE_GMII && -- state->interface != PHY_INTERFACE_MODE_SGMII && -- state->interface != PHY_INTERFACE_MODE_QSGMII) { -- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -- return; -- } -- -- /* No half-duplex. */ -- phylink_set_port_modes(mask); -- phylink_set(mask, Autoneg); -- phylink_set(mask, Pause); -- phylink_set(mask, Asym_Pause); -- phylink_set(mask, 10baseT_Full); -- phylink_set(mask, 100baseT_Full); -- phylink_set(mask, 1000baseT_Full); -- phylink_set(mask, 2500baseT_Full); -- -- bitmap_and(supported, supported, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); -- bitmap_and(state->advertising, state->advertising, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); -+ if (ocelot->ops->pcs_validate) -+ ocelot->ops->pcs_validate(ocelot, port, supported, state); - } - EXPORT_SYMBOL(ocelot_phylink_validate); - - void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, - struct phylink_link_state *state) - { -- state->link = 1; -+ if (ocelot->ops->pcs_link_state) -+ ocelot->ops->pcs_link_state(ocelot, port, state); -+ else -+ state->link = 1; - } - EXPORT_SYMBOL(ocelot_phylink_mac_pcs_get_state); - - void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port) - { -- /* Not supported */ -+ if (ocelot->ops->pcs_an_restart) -+ ocelot->ops->pcs_an_restart(ocelot, port); - } - EXPORT_SYMBOL(ocelot_phylink_mac_an_restart); - -@@ -490,7 +472,7 @@ void ocelot_phylink_mac_config(struct oc - ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); - - if (ocelot->ops->pcs_init) -- ocelot->ops->pcs_init(ocelot, port); -+ ocelot->ops->pcs_init(ocelot, port, link_an_mode, state); - - /* Enable MAC module */ - ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | ---- a/drivers/net/ethernet/mscc/ocelot_board.c -+++ b/drivers/net/ethernet/mscc/ocelot_board.c -@@ -212,7 +212,9 @@ static const struct of_device_id mscc_oc - }; - MODULE_DEVICE_TABLE(of, mscc_ocelot_match); - --static void ocelot_port_pcs_init(struct ocelot *ocelot, int port) -+static void ocelot_port_pcs_init(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) - { - struct ocelot_port *ocelot_port = ocelot->ports[port]; - -@@ -235,6 +237,36 @@ static void ocelot_port_pcs_init(struct - ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); - } - -+void ocelot_port_pcs_validate(struct ocelot *ocelot, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state) -+{ -+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -+ -+ if (state->interface != PHY_INTERFACE_MODE_NA && -+ state->interface != PHY_INTERFACE_MODE_GMII && -+ state->interface != PHY_INTERFACE_MODE_SGMII && -+ state->interface != PHY_INTERFACE_MODE_QSGMII) { -+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -+ return; -+ } -+ -+ /* No half-duplex. */ -+ phylink_set_port_modes(mask); -+ phylink_set(mask, Autoneg); -+ phylink_set(mask, Pause); -+ phylink_set(mask, Asym_Pause); -+ phylink_set(mask, 10baseT_Full); -+ phylink_set(mask, 100baseT_Full); -+ phylink_set(mask, 1000baseT_Full); -+ phylink_set(mask, 2500baseT_Full); -+ -+ bitmap_and(supported, supported, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+ bitmap_and(state->advertising, state->advertising, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ - static int ocelot_reset(struct ocelot *ocelot) - { - int retries = 100; -@@ -260,6 +292,7 @@ static int ocelot_reset(struct ocelot *o - - static const struct ocelot_ops ocelot_ops = { - .pcs_init = ocelot_port_pcs_init, -+ .pcs_validate = ocelot_port_pcs_validate, - .reset = ocelot_reset, - }; - ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -412,7 +412,15 @@ enum { - struct ocelot; - - struct ocelot_ops { -- void (*pcs_init)(struct ocelot *ocelot, int port); -+ void (*pcs_init)(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state); -+ void (*pcs_an_restart)(struct ocelot *ocelot, int port); -+ void (*pcs_link_state)(struct ocelot *ocelot, int port, -+ struct phylink_link_state *state); -+ void (*pcs_validate)(struct ocelot *ocelot, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state); - int (*reset)(struct ocelot *ocelot); - }; - -@@ -479,8 +487,6 @@ struct ocelot { - struct mutex ptp_lock; - /* Protects the PTP clock */ - spinlock_t ptp_clock_lock; -- -- void (*port_pcs_init)(struct ocelot_port *port); - }; - - #define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) diff --git a/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch b/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch deleted file mode 100644 index e2de403c2d..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0283-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch +++ /dev/null @@ -1,442 +0,0 @@ -From 0e219a6aa07754d81a25c9e4408d81d194cd2000 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Fri, 22 Nov 2019 13:45:52 +0200 -Subject: [PATCH] net: dsa: felix: Add PCS operations for PHYLINK - -This removes the bootloader dependency for SGMII PCS pre-configuration, -as well as adds support for monitoring the in-band SGMII AN between the -PCS and the system-side link partner (PHY or other MAC). - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 23 ++- - drivers/net/dsa/ocelot/felix.h | 7 +- - drivers/net/dsa/ocelot/felix_vsc9959.c | 292 ++++++++++++++++++++++++++++++++- - 3 files changed, 314 insertions(+), 8 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -265,7 +265,7 @@ static int felix_get_ts_info(struct dsa_ - static int felix_init_structs(struct felix *felix, int num_phys_ports) - { - struct ocelot *ocelot = &felix->ocelot; -- resource_size_t base; -+ resource_size_t switch_base; - int port, i, err; - - ocelot->num_phys_ports = num_phys_ports; -@@ -281,7 +281,8 @@ static int felix_init_structs(struct fel - ocelot->ops = felix->info->ops; - ocelot->quirks = felix->info->quirks; - -- base = pci_resource_start(felix->pdev, felix->info->pci_bar); -+ switch_base = pci_resource_start(felix->pdev, -+ felix->info->switch_pci_bar); - - for (i = 0; i < TARGET_MAX; i++) { - struct regmap *target; -@@ -292,8 +293,8 @@ static int felix_init_structs(struct fel - - res = &felix->info->target_io_res[i]; - res->flags = IORESOURCE_MEM; -- res->start += base; -- res->end += base; -+ res->start += switch_base; -+ res->end += switch_base; - - target = ocelot_regmap_init(ocelot, res); - if (IS_ERR(target)) { -@@ -327,8 +328,8 @@ static int felix_init_structs(struct fel - - res = &felix->info->port_io_res[port]; - res->flags = IORESOURCE_MEM; -- res->start += base; -- res->end += base; -+ res->start += switch_base; -+ res->end += switch_base; - - port_regs = devm_ioremap_resource(ocelot->dev, res); - if (IS_ERR(port_regs)) { -@@ -342,6 +343,12 @@ static int felix_init_structs(struct fel - ocelot->ports[port] = ocelot_port; - } - -+ if (felix->info->mdio_bus_alloc) { -+ err = felix->info->mdio_bus_alloc(ocelot); -+ if (err < 0) -+ return err; -+ } -+ - return 0; - } - -@@ -377,6 +384,10 @@ static int felix_setup(struct dsa_switch - static void felix_teardown(struct dsa_switch *ds) - { - struct ocelot *ocelot = ds->priv; -+ struct felix *felix = ocelot_to_felix(ocelot); -+ -+ if (felix->imdio) -+ mdiobus_unregister(felix->imdio); - - /* stop workqueue thread */ - ocelot_deinit(ocelot); ---- a/drivers/net/dsa/ocelot/felix.h -+++ b/drivers/net/dsa/ocelot/felix.h -@@ -10,6 +10,7 @@ - struct felix_info { - struct resource *target_io_res; - struct resource *port_io_res; -+ struct resource *imdio_res; - const struct reg_field *regfields; - const u32 *const *map; - const struct ocelot_ops *ops; -@@ -17,8 +18,10 @@ struct felix_info { - const struct ocelot_stat_layout *stats_layout; - unsigned int num_stats; - int num_ports; -- int pci_bar; -+ int switch_pci_bar; -+ int imdio_pci_bar; - unsigned long quirks; -+ int (*mdio_bus_alloc)(struct ocelot *ocelot); - }; - - extern struct felix_info felix_info_vsc9959; -@@ -33,6 +36,8 @@ struct felix { - struct pci_dev *pdev; - struct felix_info *info; - struct ocelot ocelot; -+ struct mii_bus *imdio; -+ struct phy_device **pcs; - }; - - #endif ---- a/drivers/net/dsa/ocelot/felix_vsc9959.c -+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c -@@ -2,6 +2,7 @@ - /* Copyright 2017 Microsemi Corporation - * Copyright 2018-2019 NXP Semiconductors - */ -+#include <linux/fsl/enetc_mdio.h> - #include <soc/mscc/ocelot_sys.h> - #include <soc/mscc/ocelot.h> - #include <linux/iopoll.h> -@@ -390,6 +391,15 @@ static struct resource vsc9959_port_io_r - }, - }; - -+/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an -+ * SGMII/QSGMII MAC PCS can be found. -+ */ -+static struct resource vsc9959_imdio_res = { -+ .start = 0x8030, -+ .end = 0x8040, -+ .name = "imdio", -+}; -+ - static const struct reg_field vsc9959_regfields[] = { - [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6), - [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5), -@@ -569,13 +579,291 @@ static int vsc9959_reset(struct ocelot * - return 0; - } - -+void vsc9959_pcs_validate(struct ocelot *ocelot, int port, -+ unsigned long *supported, -+ struct phylink_link_state *state) -+{ -+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -+ -+ if (state->interface != PHY_INTERFACE_MODE_NA && -+ state->interface != PHY_INTERFACE_MODE_GMII && -+ state->interface != PHY_INTERFACE_MODE_SGMII && -+ state->interface != PHY_INTERFACE_MODE_QSGMII && -+ state->interface != PHY_INTERFACE_MODE_USXGMII) { -+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -+ return; -+ } -+ -+ /* No half-duplex. */ -+ phylink_set_port_modes(mask); -+ phylink_set(mask, Autoneg); -+ phylink_set(mask, Pause); -+ phylink_set(mask, Asym_Pause); -+ phylink_set(mask, 10baseT_Full); -+ phylink_set(mask, 100baseT_Full); -+ phylink_set(mask, 1000baseT_Full); -+ phylink_set(mask, 1000baseX_Full); -+ phylink_set(mask, 2500baseT_Full); -+ phylink_set(mask, 2500baseX_Full); -+ phylink_set(mask, 1000baseKX_Full); -+ -+ bitmap_and(supported, supported, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+ bitmap_and(state->advertising, state->advertising, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+} -+ -+static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port) -+{ -+ struct felix *felix = ocelot_to_felix(ocelot); -+ struct phy_device *pcs = felix->pcs[port]; -+ -+ if (!pcs) -+ return; -+ -+ phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART); -+} -+ -+static void vsc9959_pcs_init_sgmii(struct phy_device *pcs, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) -+{ -+ /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 -+ * for the MAC PCS in order to acknowledge the AN. -+ */ -+ phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK); -+ -+ /* Set to SGMII mode, use AN */ -+ phy_write(pcs, ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN); -+ -+ /* Adjust link timer for SGMII */ -+ phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); -+ phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); -+ -+ phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | -+ BMCR_FULLDPLX | -+ BMCR_ANENABLE); -+} -+ -+#define ADVERTISE_USXGMII_FDX BIT(12) -+ -+static void vsc9959_pcs_init_sxgmii(struct phy_device *pcs, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) -+{ -+ /* Configure device ability for the USXGMII Replicator */ -+ phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, -+ ADVERTISE_SGMII | -+ ADVERTISE_LPACK | -+ ADVERTISE_USXGMII_FDX); -+} -+ -+static void vsc9959_pcs_init(struct ocelot *ocelot, int port, -+ unsigned int link_an_mode, -+ const struct phylink_link_state *state) -+{ -+ struct felix *felix = ocelot_to_felix(ocelot); -+ struct phy_device *pcs = felix->pcs[port]; -+ -+ if (!pcs) -+ return; -+ -+ if (link_an_mode == MLO_AN_FIXED) { -+ phydev_err(pcs, "Fixed modes are not yet supported.\n"); -+ return; -+ } -+ -+ pcs->interface = state->interface; -+ if (pcs->interface == PHY_INTERFACE_MODE_USXGMII) -+ pcs->is_c45 = true; -+ else -+ pcs->is_c45 = false; -+ -+ /* The PCS does not implement the BMSR register fully, so capability -+ * detection via genphy_read_abilities does not work. Since we can get -+ * the PHY config word from the LPA register though, there is still -+ * value in using the generic phy_resolve_aneg_linkmode function. So -+ * populate the supported and advertising link modes manually here. -+ */ -+ linkmode_set_bit_array(phy_basic_ports_array, -+ ARRAY_SIZE(phy_basic_ports_array), -+ pcs->supported); -+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pcs->supported); -+ linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); -+ linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); -+ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); -+ phy_advertise_supported(pcs); -+ -+ switch (pcs->interface) { -+ case PHY_INTERFACE_MODE_SGMII: -+ case PHY_INTERFACE_MODE_QSGMII: -+ vsc9959_pcs_init_sgmii(pcs, link_an_mode, state); -+ break; -+ case PHY_INTERFACE_MODE_USXGMII: -+ vsc9959_pcs_init_sxgmii(pcs, link_an_mode, state); -+ break; -+ default: -+ dev_err(ocelot->dev, "Unsupported link mode %s\n", -+ phy_modes(pcs->interface)); -+ } -+} -+ -+static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, -+ struct phylink_link_state *state) -+{ -+ struct felix *felix = ocelot_to_felix(ocelot); -+ struct phy_device *pcs = felix->pcs[port]; -+ int err; -+ -+ if (!pcs) -+ return; -+ -+ /* Reading PCS status not yet supported for USXGMII */ -+ if (pcs->is_c45) { -+ state->link = 1; -+ return; -+ } -+ -+ pcs->speed = SPEED_UNKNOWN; -+ pcs->duplex = DUPLEX_UNKNOWN; -+ pcs->pause = 0; -+ pcs->asym_pause = 0; -+ -+ err = genphy_update_link(pcs); -+ if (err < 0) -+ return; -+ -+ if (pcs->autoneg_complete) { -+ u16 lpa = phy_read(pcs, MII_LPA); -+ -+ switch (state->interface) { -+ case PHY_INTERFACE_MODE_SGMII: -+ case PHY_INTERFACE_MODE_QSGMII: -+ mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa); -+ break; -+ default: -+ return; -+ } -+ -+ phy_resolve_aneg_linkmode(pcs); -+ } -+ -+ state->an_complete = pcs->autoneg_complete; -+ state->an_enabled = pcs->autoneg; -+ state->link = pcs->link; -+ state->duplex = pcs->duplex; -+ state->speed = pcs->speed; -+ /* SGMII AN does not negotiate flow control, but that's ok, -+ * since phylink already knows that, and does: -+ * link_state.pause |= pl->phy_state.pause; -+ */ -+ state->pause = pcs->pause; -+ -+ dev_dbg(ocelot->dev, -+ "%s: mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n", -+ __func__, -+ phy_modes(state->interface), -+ phy_speed_to_str(state->speed), -+ phy_duplex_to_str(state->duplex), -+ __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, -+ __ETHTOOL_LINK_MODE_MASK_NBITS, state->lp_advertising, -+ state->link, state->an_enabled, state->an_complete); -+} -+ - static const struct ocelot_ops vsc9959_ops = { - .reset = vsc9959_reset, -+ .pcs_init = vsc9959_pcs_init, -+ .pcs_an_restart = vsc9959_pcs_an_restart, -+ .pcs_link_state = vsc9959_pcs_link_state, -+ .pcs_validate = vsc9959_pcs_validate, - }; - -+static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) -+{ -+ struct felix *felix = ocelot_to_felix(ocelot); -+ struct enetc_mdio_priv *mdio_priv; -+ struct device *dev = ocelot->dev; -+ resource_size_t imdio_base; -+ void __iomem *imdio_regs; -+ struct resource *res; -+ struct enetc_hw *hw; -+ struct mii_bus *bus; -+ int port; -+ int rc; -+ -+ felix->pcs = devm_kcalloc(dev, felix->info->num_ports, -+ sizeof(struct phy_device), -+ GFP_KERNEL); -+ if (!felix->pcs) { -+ dev_err(dev, "failed to allocate array for PCS PHYs\n"); -+ return -ENOMEM; -+ } -+ -+ imdio_base = pci_resource_start(felix->pdev, -+ felix->info->imdio_pci_bar); -+ -+ res = felix->info->imdio_res; -+ res->flags = IORESOURCE_MEM; -+ res->start += imdio_base; -+ res->end += imdio_base; -+ -+ imdio_regs = devm_ioremap_resource(dev, res); -+ if (IS_ERR(imdio_regs)) { -+ dev_err(dev, "failed to map internal MDIO registers\n"); -+ return PTR_ERR(imdio_regs); -+ } -+ -+ hw = enetc_hw_alloc(dev, imdio_regs); -+ if (IS_ERR(hw)) { -+ dev_err(dev, "failed to allocate ENETC HW structure\n"); -+ return PTR_ERR(hw); -+ } -+ -+ bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); -+ if (!bus) -+ return -ENOMEM; -+ -+ bus->name = "VSC9959 internal MDIO bus"; -+ bus->read = enetc_mdio_read; -+ bus->write = enetc_mdio_write; -+ bus->parent = dev; -+ mdio_priv = bus->priv; -+ mdio_priv->hw = hw; -+ /* This gets added to imdio_regs, which already maps addresses -+ * starting with the proper offset. -+ */ -+ mdio_priv->mdio_base = 0; -+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); -+ -+ /* Needed in order to initialize the bus mutex lock */ -+ rc = mdiobus_register(bus); -+ if (rc < 0) { -+ dev_err(dev, "failed to register MDIO bus\n"); -+ return rc; -+ } -+ -+ felix->imdio = bus; -+ -+ for (port = 0; port < felix->info->num_ports; port++) { -+ struct phy_device *pcs; -+ bool is_c45 = false; -+ -+ pcs = get_phy_device(felix->imdio, port, is_c45); -+ if (IS_ERR(pcs)) -+ continue; -+ -+ felix->pcs[port] = pcs; -+ -+ dev_info(dev, "Found PCS at internal MDIO address %d\n", port); -+ } -+ -+ return 0; -+} -+ - struct felix_info felix_info_vsc9959 = { - .target_io_res = vsc9959_target_io_res, - .port_io_res = vsc9959_port_io_res, -+ .imdio_res = &vsc9959_imdio_res, - .regfields = vsc9959_regfields, - .map = vsc9959_regmap, - .ops = &vsc9959_ops, -@@ -583,6 +871,8 @@ struct felix_info felix_info_vsc9959 = { - .num_stats = ARRAY_SIZE(vsc9959_stats_layout), - .shared_queue_sz = 128 * 1024, - .num_ports = 6, -- .pci_bar = 4, -+ .switch_pci_bar = 4, -+ .imdio_pci_bar = 0, - .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION, -+ .mdio_bus_alloc = vsc9959_mdio_bus_alloc, - }; diff --git a/target/linux/layerscape/patches-5.4/701-net-0338-net-dsa-felix-Fix-probing-allocation-and-cleanup-pat.patch b/target/linux/layerscape/patches-5.4/701-net-0338-net-dsa-felix-Fix-probing-allocation-and-cleanup-pat.patch index 3e99861d16..ec4c62a7a5 100644 --- a/target/linux/layerscape/patches-5.4/701-net-0338-net-dsa-felix-Fix-probing-allocation-and-cleanup-pat.patch +++ b/target/linux/layerscape/patches-5.4/701-net-0338-net-dsa-felix-Fix-probing-allocation-and-cleanup-pat.patch @@ -90,7 +90,7 @@ Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com> --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c -@@ -530,7 +530,7 @@ static int felix_pci_probe(struct pci_de +@@ -485,7 +485,7 @@ static int felix_pci_probe(struct pci_de } } @@ -99,7 +99,7 @@ Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com> if (!felix) { err = -ENOMEM; dev_err(&pdev->dev, "Failed to allocate driver memory\n"); -@@ -577,11 +577,9 @@ static int felix_pci_probe(struct pci_de +@@ -532,11 +532,9 @@ static int felix_pci_probe(struct pci_de return 0; err_register_ds: @@ -111,7 +111,7 @@ Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com> err_dma: pci_disable_device(pdev); err_pci_enable: -@@ -596,9 +594,6 @@ static void felix_pci_remove(struct pci_ +@@ -551,9 +549,6 @@ static void felix_pci_remove(struct pci_ dsa_unregister_switch(felix->ds); diff --git a/target/linux/layerscape/patches-5.4/701-net-0341-LF-368-net-mscc-ocelot-add-VCAP-IS2-rule-to-trap-PTP.patch b/target/linux/layerscape/patches-5.4/701-net-0341-LF-368-net-mscc-ocelot-add-VCAP-IS2-rule-to-trap-PTP.patch index af80b9f666..0b4cb96955 100644 --- a/target/linux/layerscape/patches-5.4/701-net-0341-LF-368-net-mscc-ocelot-add-VCAP-IS2-rule-to-trap-PTP.patch +++ b/target/linux/layerscape/patches-5.4/701-net-0341-LF-368-net-mscc-ocelot-add-VCAP-IS2-rule-to-trap-PTP.patch @@ -14,7 +14,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -2338,6 +2338,20 @@ void ocelot_set_cpu_port(struct ocelot * +@@ -2342,6 +2342,20 @@ void ocelot_set_cpu_port(struct ocelot * } EXPORT_SYMBOL(ocelot_set_cpu_port); @@ -35,7 +35,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> int ocelot_init(struct ocelot *ocelot) { char queue_name[32]; -@@ -2475,6 +2489,13 @@ int ocelot_init(struct ocelot *ocelot) +@@ -2479,6 +2493,13 @@ int ocelot_init(struct ocelot *ocelot) "Timestamp initialization failed\n"); return ret; } @@ -49,7 +49,7 @@ Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> } return 0; -@@ -2489,6 +2510,8 @@ void ocelot_deinit(struct ocelot *ocelot +@@ -2493,6 +2514,8 @@ void ocelot_deinit(struct ocelot *ocelot cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); mutex_destroy(&ocelot->stats_lock); diff --git a/target/linux/layerscape/patches-5.4/701-net-0367-net-mscc-ocelot-Workaround-to-allow-traffic-to-CPU-i.patch b/target/linux/layerscape/patches-5.4/701-net-0367-net-mscc-ocelot-Workaround-to-allow-traffic-to-CPU-i.patch index 069c518389..f2d69b6333 100644 --- a/target/linux/layerscape/patches-5.4/701-net-0367-net-mscc-ocelot-Workaround-to-allow-traffic-to-CPU-i.patch +++ b/target/linux/layerscape/patches-5.4/701-net-0367-net-mscc-ocelot-Workaround-to-allow-traffic-to-CPU-i.patch @@ -128,7 +128,7 @@ Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -2292,6 +2292,18 @@ void ocelot_set_cpu_port(struct ocelot * +@@ -2296,6 +2296,18 @@ void ocelot_set_cpu_port(struct ocelot * enum ocelot_tag_prefix injection, enum ocelot_tag_prefix extraction) { diff --git a/target/linux/layerscape/patches-5.4/701-net-0368-Revert-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch b/target/linux/layerscape/patches-5.4/701-net-0368-Revert-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch deleted file mode 100644 index ea01a8b24b..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0368-Revert-net-dsa-felix-Add-PCS-operations-for-PHYLINK.patch +++ /dev/null @@ -1,440 +0,0 @@ -From 9ee918eb911853c7b46cd84779d857988366e845 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:30:24 +0200 -Subject: [PATCH] Revert "net: dsa: felix: Add PCS operations for PHYLINK" - -This reverts commit 1082a3ef9e832cc52c4b0053c7c52453376f4830. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 23 +-- - drivers/net/dsa/ocelot/felix.h | 7 +- - drivers/net/dsa/ocelot/felix_vsc9959.c | 292 +-------------------------------- - 3 files changed, 8 insertions(+), 314 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -265,7 +265,7 @@ static int felix_get_ts_info(struct dsa_ - static int felix_init_structs(struct felix *felix, int num_phys_ports) - { - struct ocelot *ocelot = &felix->ocelot; -- resource_size_t switch_base; -+ resource_size_t base; - int port, i, err; - - ocelot->num_phys_ports = num_phys_ports; -@@ -281,8 +281,7 @@ static int felix_init_structs(struct fel - ocelot->ops = felix->info->ops; - ocelot->quirks = felix->info->quirks; - -- switch_base = pci_resource_start(felix->pdev, -- felix->info->switch_pci_bar); -+ base = pci_resource_start(felix->pdev, felix->info->pci_bar); - - for (i = 0; i < TARGET_MAX; i++) { - struct regmap *target; -@@ -293,8 +292,8 @@ static int felix_init_structs(struct fel - - res = &felix->info->target_io_res[i]; - res->flags = IORESOURCE_MEM; -- res->start += switch_base; -- res->end += switch_base; -+ res->start += base; -+ res->end += base; - - target = ocelot_regmap_init(ocelot, res); - if (IS_ERR(target)) { -@@ -328,8 +327,8 @@ static int felix_init_structs(struct fel - - res = &felix->info->port_io_res[port]; - res->flags = IORESOURCE_MEM; -- res->start += switch_base; -- res->end += switch_base; -+ res->start += base; -+ res->end += base; - - port_regs = devm_ioremap_resource(ocelot->dev, res); - if (IS_ERR(port_regs)) { -@@ -343,12 +342,6 @@ static int felix_init_structs(struct fel - ocelot->ports[port] = ocelot_port; - } - -- if (felix->info->mdio_bus_alloc) { -- err = felix->info->mdio_bus_alloc(ocelot); -- if (err < 0) -- return err; -- } -- - return 0; - } - -@@ -384,10 +377,6 @@ static int felix_setup(struct dsa_switch - static void felix_teardown(struct dsa_switch *ds) - { - struct ocelot *ocelot = ds->priv; -- struct felix *felix = ocelot_to_felix(ocelot); -- -- if (felix->imdio) -- mdiobus_unregister(felix->imdio); - - /* stop workqueue thread */ - ocelot_deinit(ocelot); ---- a/drivers/net/dsa/ocelot/felix.h -+++ b/drivers/net/dsa/ocelot/felix.h -@@ -10,7 +10,6 @@ - struct felix_info { - struct resource *target_io_res; - struct resource *port_io_res; -- struct resource *imdio_res; - const struct reg_field *regfields; - const u32 *const *map; - const struct ocelot_ops *ops; -@@ -18,10 +17,8 @@ struct felix_info { - const struct ocelot_stat_layout *stats_layout; - unsigned int num_stats; - int num_ports; -- int switch_pci_bar; -- int imdio_pci_bar; -+ int pci_bar; - unsigned long quirks; -- int (*mdio_bus_alloc)(struct ocelot *ocelot); - }; - - extern struct felix_info felix_info_vsc9959; -@@ -36,8 +33,6 @@ struct felix { - struct pci_dev *pdev; - struct felix_info *info; - struct ocelot ocelot; -- struct mii_bus *imdio; -- struct phy_device **pcs; - }; - - #endif ---- a/drivers/net/dsa/ocelot/felix_vsc9959.c -+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c -@@ -2,7 +2,6 @@ - /* Copyright 2017 Microsemi Corporation - * Copyright 2018-2019 NXP Semiconductors - */ --#include <linux/fsl/enetc_mdio.h> - #include <soc/mscc/ocelot_sys.h> - #include <soc/mscc/ocelot.h> - #include <linux/iopoll.h> -@@ -391,15 +390,6 @@ static struct resource vsc9959_port_io_r - }, - }; - --/* Port MAC 0 Internal MDIO bus through which the SerDes acting as an -- * SGMII/QSGMII MAC PCS can be found. -- */ --static struct resource vsc9959_imdio_res = { -- .start = 0x8030, -- .end = 0x8040, -- .name = "imdio", --}; -- - static const struct reg_field vsc9959_regfields[] = { - [ANA_ADVLEARN_VLAN_CHK] = REG_FIELD(ANA_ADVLEARN, 6, 6), - [ANA_ADVLEARN_LEARN_MIRROR] = REG_FIELD(ANA_ADVLEARN, 0, 5), -@@ -579,291 +569,13 @@ static int vsc9959_reset(struct ocelot * - return 0; - } - --void vsc9959_pcs_validate(struct ocelot *ocelot, int port, -- unsigned long *supported, -- struct phylink_link_state *state) --{ -- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -- -- if (state->interface != PHY_INTERFACE_MODE_NA && -- state->interface != PHY_INTERFACE_MODE_GMII && -- state->interface != PHY_INTERFACE_MODE_SGMII && -- state->interface != PHY_INTERFACE_MODE_QSGMII && -- state->interface != PHY_INTERFACE_MODE_USXGMII) { -- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -- return; -- } -- -- /* No half-duplex. */ -- phylink_set_port_modes(mask); -- phylink_set(mask, Autoneg); -- phylink_set(mask, Pause); -- phylink_set(mask, Asym_Pause); -- phylink_set(mask, 10baseT_Full); -- phylink_set(mask, 100baseT_Full); -- phylink_set(mask, 1000baseT_Full); -- phylink_set(mask, 1000baseX_Full); -- phylink_set(mask, 2500baseT_Full); -- phylink_set(mask, 2500baseX_Full); -- phylink_set(mask, 1000baseKX_Full); -- -- bitmap_and(supported, supported, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); -- bitmap_and(state->advertising, state->advertising, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); --} -- --static void vsc9959_pcs_an_restart(struct ocelot *ocelot, int port) --{ -- struct felix *felix = ocelot_to_felix(ocelot); -- struct phy_device *pcs = felix->pcs[port]; -- -- if (!pcs) -- return; -- -- phy_set_bits(pcs, MII_BMCR, BMCR_ANRESTART); --} -- --static void vsc9959_pcs_init_sgmii(struct phy_device *pcs, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) --{ -- /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 -- * for the MAC PCS in order to acknowledge the AN. -- */ -- phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK); -- -- /* Set to SGMII mode, use AN */ -- phy_write(pcs, ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN); -- -- /* Adjust link timer for SGMII */ -- phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); -- phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); -- -- phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | -- BMCR_FULLDPLX | -- BMCR_ANENABLE); --} -- --#define ADVERTISE_USXGMII_FDX BIT(12) -- --static void vsc9959_pcs_init_sxgmii(struct phy_device *pcs, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) --{ -- /* Configure device ability for the USXGMII Replicator */ -- phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, -- ADVERTISE_SGMII | -- ADVERTISE_LPACK | -- ADVERTISE_USXGMII_FDX); --} -- --static void vsc9959_pcs_init(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) --{ -- struct felix *felix = ocelot_to_felix(ocelot); -- struct phy_device *pcs = felix->pcs[port]; -- -- if (!pcs) -- return; -- -- if (link_an_mode == MLO_AN_FIXED) { -- phydev_err(pcs, "Fixed modes are not yet supported.\n"); -- return; -- } -- -- pcs->interface = state->interface; -- if (pcs->interface == PHY_INTERFACE_MODE_USXGMII) -- pcs->is_c45 = true; -- else -- pcs->is_c45 = false; -- -- /* The PCS does not implement the BMSR register fully, so capability -- * detection via genphy_read_abilities does not work. Since we can get -- * the PHY config word from the LPA register though, there is still -- * value in using the generic phy_resolve_aneg_linkmode function. So -- * populate the supported and advertising link modes manually here. -- */ -- linkmode_set_bit_array(phy_basic_ports_array, -- ARRAY_SIZE(phy_basic_ports_array), -- pcs->supported); -- linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pcs->supported); -- linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); -- linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); -- linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); -- phy_advertise_supported(pcs); -- -- switch (pcs->interface) { -- case PHY_INTERFACE_MODE_SGMII: -- case PHY_INTERFACE_MODE_QSGMII: -- vsc9959_pcs_init_sgmii(pcs, link_an_mode, state); -- break; -- case PHY_INTERFACE_MODE_USXGMII: -- vsc9959_pcs_init_sxgmii(pcs, link_an_mode, state); -- break; -- default: -- dev_err(ocelot->dev, "Unsupported link mode %s\n", -- phy_modes(pcs->interface)); -- } --} -- --static void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, -- struct phylink_link_state *state) --{ -- struct felix *felix = ocelot_to_felix(ocelot); -- struct phy_device *pcs = felix->pcs[port]; -- int err; -- -- if (!pcs) -- return; -- -- /* Reading PCS status not yet supported for USXGMII */ -- if (pcs->is_c45) { -- state->link = 1; -- return; -- } -- -- pcs->speed = SPEED_UNKNOWN; -- pcs->duplex = DUPLEX_UNKNOWN; -- pcs->pause = 0; -- pcs->asym_pause = 0; -- -- err = genphy_update_link(pcs); -- if (err < 0) -- return; -- -- if (pcs->autoneg_complete) { -- u16 lpa = phy_read(pcs, MII_LPA); -- -- switch (state->interface) { -- case PHY_INTERFACE_MODE_SGMII: -- case PHY_INTERFACE_MODE_QSGMII: -- mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa); -- break; -- default: -- return; -- } -- -- phy_resolve_aneg_linkmode(pcs); -- } -- -- state->an_complete = pcs->autoneg_complete; -- state->an_enabled = pcs->autoneg; -- state->link = pcs->link; -- state->duplex = pcs->duplex; -- state->speed = pcs->speed; -- /* SGMII AN does not negotiate flow control, but that's ok, -- * since phylink already knows that, and does: -- * link_state.pause |= pl->phy_state.pause; -- */ -- state->pause = pcs->pause; -- -- dev_dbg(ocelot->dev, -- "%s: mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n", -- __func__, -- phy_modes(state->interface), -- phy_speed_to_str(state->speed), -- phy_duplex_to_str(state->duplex), -- __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, -- __ETHTOOL_LINK_MODE_MASK_NBITS, state->lp_advertising, -- state->link, state->an_enabled, state->an_complete); --} -- - static const struct ocelot_ops vsc9959_ops = { - .reset = vsc9959_reset, -- .pcs_init = vsc9959_pcs_init, -- .pcs_an_restart = vsc9959_pcs_an_restart, -- .pcs_link_state = vsc9959_pcs_link_state, -- .pcs_validate = vsc9959_pcs_validate, - }; - --static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) --{ -- struct felix *felix = ocelot_to_felix(ocelot); -- struct enetc_mdio_priv *mdio_priv; -- struct device *dev = ocelot->dev; -- resource_size_t imdio_base; -- void __iomem *imdio_regs; -- struct resource *res; -- struct enetc_hw *hw; -- struct mii_bus *bus; -- int port; -- int rc; -- -- felix->pcs = devm_kcalloc(dev, felix->info->num_ports, -- sizeof(struct phy_device), -- GFP_KERNEL); -- if (!felix->pcs) { -- dev_err(dev, "failed to allocate array for PCS PHYs\n"); -- return -ENOMEM; -- } -- -- imdio_base = pci_resource_start(felix->pdev, -- felix->info->imdio_pci_bar); -- -- res = felix->info->imdio_res; -- res->flags = IORESOURCE_MEM; -- res->start += imdio_base; -- res->end += imdio_base; -- -- imdio_regs = devm_ioremap_resource(dev, res); -- if (IS_ERR(imdio_regs)) { -- dev_err(dev, "failed to map internal MDIO registers\n"); -- return PTR_ERR(imdio_regs); -- } -- -- hw = enetc_hw_alloc(dev, imdio_regs); -- if (IS_ERR(hw)) { -- dev_err(dev, "failed to allocate ENETC HW structure\n"); -- return PTR_ERR(hw); -- } -- -- bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); -- if (!bus) -- return -ENOMEM; -- -- bus->name = "VSC9959 internal MDIO bus"; -- bus->read = enetc_mdio_read; -- bus->write = enetc_mdio_write; -- bus->parent = dev; -- mdio_priv = bus->priv; -- mdio_priv->hw = hw; -- /* This gets added to imdio_regs, which already maps addresses -- * starting with the proper offset. -- */ -- mdio_priv->mdio_base = 0; -- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); -- -- /* Needed in order to initialize the bus mutex lock */ -- rc = mdiobus_register(bus); -- if (rc < 0) { -- dev_err(dev, "failed to register MDIO bus\n"); -- return rc; -- } -- -- felix->imdio = bus; -- -- for (port = 0; port < felix->info->num_ports; port++) { -- struct phy_device *pcs; -- bool is_c45 = false; -- -- pcs = get_phy_device(felix->imdio, port, is_c45); -- if (IS_ERR(pcs)) -- continue; -- -- felix->pcs[port] = pcs; -- -- dev_info(dev, "Found PCS at internal MDIO address %d\n", port); -- } -- -- return 0; --} -- - struct felix_info felix_info_vsc9959 = { - .target_io_res = vsc9959_target_io_res, - .port_io_res = vsc9959_port_io_res, -- .imdio_res = &vsc9959_imdio_res, - .regfields = vsc9959_regfields, - .map = vsc9959_regmap, - .ops = &vsc9959_ops, -@@ -871,8 +583,6 @@ struct felix_info felix_info_vsc9959 = { - .num_stats = ARRAY_SIZE(vsc9959_stats_layout), - .shared_queue_sz = 128 * 1024, - .num_ports = 6, -- .switch_pci_bar = 4, -- .imdio_pci_bar = 0, -+ .pci_bar = 4, - .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION, -- .mdio_bus_alloc = vsc9959_mdio_bus_alloc, - }; diff --git a/target/linux/layerscape/patches-5.4/701-net-0369-Revert-net-mscc-ocelot-introduce-more-focused-PCS-op.patch b/target/linux/layerscape/patches-5.4/701-net-0369-Revert-net-mscc-ocelot-introduce-more-focused-PCS-op.patch deleted file mode 100644 index 9ba2030314..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0369-Revert-net-mscc-ocelot-introduce-more-focused-PCS-op.patch +++ /dev/null @@ -1,164 +0,0 @@ -From e1aa2a770cc5f4d46693bb491ed2ca7f066c3585 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:30:41 +0200 -Subject: [PATCH] Revert "net: mscc: ocelot: introduce more focused PCS ops for - PHYLINK" - -This reverts commit 423c8b04007c85907f8f514de459ebc8541f0a54. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/mscc/ocelot.c | 36 ++++++++++++++++++++++++-------- - drivers/net/ethernet/mscc/ocelot_board.c | 35 +------------------------------ - include/soc/mscc/ocelot.h | 12 +++-------- - 3 files changed, 31 insertions(+), 52 deletions(-) - ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -410,25 +410,43 @@ void ocelot_phylink_validate(struct ocel - unsigned long *supported, - struct phylink_link_state *state) - { -- if (ocelot->ops->pcs_validate) -- ocelot->ops->pcs_validate(ocelot, port, supported, state); -+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -+ -+ if (state->interface != PHY_INTERFACE_MODE_NA && -+ state->interface != PHY_INTERFACE_MODE_GMII && -+ state->interface != PHY_INTERFACE_MODE_SGMII && -+ state->interface != PHY_INTERFACE_MODE_QSGMII) { -+ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -+ return; -+ } -+ -+ /* No half-duplex. */ -+ phylink_set_port_modes(mask); -+ phylink_set(mask, Autoneg); -+ phylink_set(mask, Pause); -+ phylink_set(mask, Asym_Pause); -+ phylink_set(mask, 10baseT_Full); -+ phylink_set(mask, 100baseT_Full); -+ phylink_set(mask, 1000baseT_Full); -+ phylink_set(mask, 2500baseT_Full); -+ -+ bitmap_and(supported, supported, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); -+ bitmap_and(state->advertising, state->advertising, mask, -+ __ETHTOOL_LINK_MODE_MASK_NBITS); - } - EXPORT_SYMBOL(ocelot_phylink_validate); - - void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, - struct phylink_link_state *state) - { -- if (ocelot->ops->pcs_link_state) -- ocelot->ops->pcs_link_state(ocelot, port, state); -- else -- state->link = 1; -+ state->link = 1; - } - EXPORT_SYMBOL(ocelot_phylink_mac_pcs_get_state); - - void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port) - { -- if (ocelot->ops->pcs_an_restart) -- ocelot->ops->pcs_an_restart(ocelot, port); -+ /* Not supported */ - } - EXPORT_SYMBOL(ocelot_phylink_mac_an_restart); - -@@ -472,7 +490,7 @@ void ocelot_phylink_mac_config(struct oc - ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); - - if (ocelot->ops->pcs_init) -- ocelot->ops->pcs_init(ocelot, port, link_an_mode, state); -+ ocelot->ops->pcs_init(ocelot, port); - - /* Enable MAC module */ - ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | ---- a/drivers/net/ethernet/mscc/ocelot_board.c -+++ b/drivers/net/ethernet/mscc/ocelot_board.c -@@ -212,9 +212,7 @@ static const struct of_device_id mscc_oc - }; - MODULE_DEVICE_TABLE(of, mscc_ocelot_match); - --static void ocelot_port_pcs_init(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) -+static void ocelot_port_pcs_init(struct ocelot *ocelot, int port) - { - struct ocelot_port *ocelot_port = ocelot->ports[port]; - -@@ -237,36 +235,6 @@ static void ocelot_port_pcs_init(struct - ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); - } - --void ocelot_port_pcs_validate(struct ocelot *ocelot, int port, -- unsigned long *supported, -- struct phylink_link_state *state) --{ -- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -- -- if (state->interface != PHY_INTERFACE_MODE_NA && -- state->interface != PHY_INTERFACE_MODE_GMII && -- state->interface != PHY_INTERFACE_MODE_SGMII && -- state->interface != PHY_INTERFACE_MODE_QSGMII) { -- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -- return; -- } -- -- /* No half-duplex. */ -- phylink_set_port_modes(mask); -- phylink_set(mask, Autoneg); -- phylink_set(mask, Pause); -- phylink_set(mask, Asym_Pause); -- phylink_set(mask, 10baseT_Full); -- phylink_set(mask, 100baseT_Full); -- phylink_set(mask, 1000baseT_Full); -- phylink_set(mask, 2500baseT_Full); -- -- bitmap_and(supported, supported, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); -- bitmap_and(state->advertising, state->advertising, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); --} -- - static int ocelot_reset(struct ocelot *ocelot) - { - int retries = 100; -@@ -292,7 +260,6 @@ static int ocelot_reset(struct ocelot *o - - static const struct ocelot_ops ocelot_ops = { - .pcs_init = ocelot_port_pcs_init, -- .pcs_validate = ocelot_port_pcs_validate, - .reset = ocelot_reset, - }; - ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -412,15 +412,7 @@ enum { - struct ocelot; - - struct ocelot_ops { -- void (*pcs_init)(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state); -- void (*pcs_an_restart)(struct ocelot *ocelot, int port); -- void (*pcs_link_state)(struct ocelot *ocelot, int port, -- struct phylink_link_state *state); -- void (*pcs_validate)(struct ocelot *ocelot, int port, -- unsigned long *supported, -- struct phylink_link_state *state); -+ void (*pcs_init)(struct ocelot *ocelot, int port); - int (*reset)(struct ocelot *ocelot); - }; - -@@ -487,6 +479,8 @@ struct ocelot { - struct mutex ptp_lock; - /* Protects the PTP clock */ - spinlock_t ptp_clock_lock; -+ -+ void (*port_pcs_init)(struct ocelot_port *port); - }; - - #define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) diff --git a/target/linux/layerscape/patches-5.4/701-net-0370-Revert-net-mscc-ocelot-convert-to-PHYLINK.patch b/target/linux/layerscape/patches-5.4/701-net-0370-Revert-net-mscc-ocelot-convert-to-PHYLINK.patch deleted file mode 100644 index 14231041f4..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0370-Revert-net-mscc-ocelot-convert-to-PHYLINK.patch +++ /dev/null @@ -1,653 +0,0 @@ -From 74e550d54d7c8142aefc06d1f00c506cd9039b6d Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:30:48 +0200 -Subject: [PATCH] Revert "net: mscc: ocelot: convert to PHYLINK" - -This reverts commit e51cc023c37902e10d1e0109ff0c6ddcce3d5c03. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 65 ++++--------- - drivers/net/ethernet/mscc/Kconfig | 2 +- - drivers/net/ethernet/mscc/ocelot.c | 150 ++++++++++++++---------------- - drivers/net/ethernet/mscc/ocelot.h | 13 ++- - drivers/net/ethernet/mscc/ocelot_board.c | 154 ++++--------------------------- - include/soc/mscc/ocelot.h | 22 +---- - 6 files changed, 117 insertions(+), 289 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -58,6 +58,14 @@ static int felix_set_ageing_time(struct - return 0; - } - -+static void felix_adjust_link(struct dsa_switch *ds, int port, -+ struct phy_device *phydev) -+{ -+ struct ocelot *ocelot = ds->priv; -+ -+ ocelot_adjust_link(ocelot, port, phydev); -+} -+ - static int felix_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) - { -@@ -177,59 +185,21 @@ static int felix_tsn_enable(struct dsa_p - } - #endif - --static void felix_phylink_validate(struct dsa_switch *ds, int port, -- unsigned long *supported, -- struct phylink_link_state *state) -+static int felix_port_enable(struct dsa_switch *ds, int port, -+ struct phy_device *phy) - { - struct ocelot *ocelot = ds->priv; - -- ocelot_phylink_validate(ocelot, port, supported, state); --} -- --static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port, -- struct phylink_link_state *state) --{ -- struct ocelot *ocelot = ds->priv; -- -- ocelot_phylink_mac_pcs_get_state(ocelot, port, state); -+ ocelot_port_enable(ocelot, port, phy); - - return 0; - } - --static void felix_phylink_mac_config(struct dsa_switch *ds, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) --{ -- struct ocelot *ocelot = ds->priv; -- -- ocelot_phylink_mac_config(ocelot, port, link_an_mode, state); --} -- --static void felix_phylink_mac_an_restart(struct dsa_switch *ds, int port) --{ -- struct ocelot *ocelot = ds->priv; -- -- ocelot_phylink_mac_an_restart(ocelot, port); --} -- --static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, -- unsigned int link_an_mode, -- phy_interface_t interface) --{ -- struct ocelot *ocelot = ds->priv; -- -- ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface); --} -- --static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, -- unsigned int link_an_mode, -- phy_interface_t interface, -- struct phy_device *phydev) -+static void felix_port_disable(struct dsa_switch *ds, int port) - { - struct ocelot *ocelot = ds->priv; - -- ocelot_phylink_mac_link_up(ocelot, port, link_an_mode, interface, -- phydev); -+ return ocelot_port_disable(ocelot, port); - } - - static void felix_get_strings(struct dsa_switch *ds, int port, -@@ -447,12 +417,9 @@ static const struct dsa_switch_ops felix - .get_ethtool_stats = felix_get_ethtool_stats, - .get_sset_count = felix_get_sset_count, - .get_ts_info = felix_get_ts_info, -- .phylink_validate = felix_phylink_validate, -- .phylink_mac_link_state = felix_phylink_mac_pcs_get_state, -- .phylink_mac_config = felix_phylink_mac_config, -- .phylink_mac_an_restart = felix_phylink_mac_an_restart, -- .phylink_mac_link_down = felix_phylink_mac_link_down, -- .phylink_mac_link_up = felix_phylink_mac_link_up, -+ .adjust_link = felix_adjust_link, -+ .port_enable = felix_port_enable, -+ .port_disable = felix_port_disable, - .port_fdb_dump = felix_fdb_dump, - .port_fdb_add = felix_fdb_add, - .port_fdb_del = felix_fdb_del, ---- a/drivers/net/ethernet/mscc/Kconfig -+++ b/drivers/net/ethernet/mscc/Kconfig -@@ -15,7 +15,7 @@ config MSCC_OCELOT_SWITCH - tristate "Ocelot switch driver" - depends on NET_SWITCHDEV - depends on HAS_IOMEM -- select PHYLINK -+ select PHYLIB - select REGMAP_MMIO - help - This driver supports the Ocelot network switch device. ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -13,7 +13,7 @@ - #include <linux/kernel.h> - #include <linux/module.h> - #include <linux/netdevice.h> --#include <linux/phylink.h> -+#include <linux/phy.h> - #include <linux/ptp_clock_kernel.h> - #include <linux/skbuff.h> - #include <linux/iopoll.h> -@@ -406,66 +406,18 @@ static u16 ocelot_wm_enc(u16 value) - return value; - } - --void ocelot_phylink_validate(struct ocelot *ocelot, int port, -- unsigned long *supported, -- struct phylink_link_state *state) --{ -- __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; -- -- if (state->interface != PHY_INTERFACE_MODE_NA && -- state->interface != PHY_INTERFACE_MODE_GMII && -- state->interface != PHY_INTERFACE_MODE_SGMII && -- state->interface != PHY_INTERFACE_MODE_QSGMII) { -- bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); -- return; -- } -- -- /* No half-duplex. */ -- phylink_set_port_modes(mask); -- phylink_set(mask, Autoneg); -- phylink_set(mask, Pause); -- phylink_set(mask, Asym_Pause); -- phylink_set(mask, 10baseT_Full); -- phylink_set(mask, 100baseT_Full); -- phylink_set(mask, 1000baseT_Full); -- phylink_set(mask, 2500baseT_Full); -- -- bitmap_and(supported, supported, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); -- bitmap_and(state->advertising, state->advertising, mask, -- __ETHTOOL_LINK_MODE_MASK_NBITS); --} --EXPORT_SYMBOL(ocelot_phylink_validate); -- --void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, -- struct phylink_link_state *state) --{ -- state->link = 1; --} --EXPORT_SYMBOL(ocelot_phylink_mac_pcs_get_state); -- --void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port) --{ -- /* Not supported */ --} --EXPORT_SYMBOL(ocelot_phylink_mac_an_restart); -- --void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) -+void ocelot_adjust_link(struct ocelot *ocelot, int port, -+ struct phy_device *phydev) - { - int speed, mac_speed, mac_mode = DEV_MAC_MODE_CFG_FDX_ENA; - struct ocelot_port *ocelot_port = ocelot->ports[port]; -- u32 mac_fc_cfg; - - if (ocelot->quirks & OCELOT_PCS_PERFORMS_RATE_ADAPTATION) - speed = SPEED_1000; - else -- speed = state->speed; -+ speed = phydev->speed; - - switch (speed) { -- case SPEED_UNKNOWN: -- return; - case SPEED_10: - mac_speed = OCELOT_SPEED_10; - break; -@@ -481,11 +433,16 @@ void ocelot_phylink_mac_config(struct oc - mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - default: -- dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n", -+ dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n", - port, speed); - return; - } - -+ phy_print_status(phydev); -+ -+ if (!phydev->link) -+ return; -+ - /* Only full duplex supported for now */ - ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); - -@@ -512,36 +469,27 @@ void ocelot_phylink_mac_config(struct oc - QSYS_SWITCH_PORT_MODE, port); - - /* Flow control */ -- mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed); -- if (state->pause & MLO_PAUSE_RX) -- mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA; -- if (state->pause & MLO_PAUSE_TX) -- mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA | -- SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | -- SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -- SYS_MAC_FC_CFG_ZERO_PAUSE_ENA; -- ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port); -- -+ ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | -+ SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA | -+ SYS_MAC_FC_CFG_ZERO_PAUSE_ENA | -+ SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -+ SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed), -+ SYS_MAC_FC_CFG, port); - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); - } --EXPORT_SYMBOL(ocelot_phylink_mac_config); -+EXPORT_SYMBOL(ocelot_adjust_link); - --void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- phy_interface_t interface) -+static void ocelot_port_adjust_link(struct net_device *dev) - { -- struct ocelot_port *ocelot_port = ocelot->ports[port]; -+ struct ocelot_port_private *priv = netdev_priv(dev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; - -- ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); -- ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, -- QSYS_SWITCH_PORT_MODE, port); -+ ocelot_adjust_link(ocelot, port, dev->phydev); - } --EXPORT_SYMBOL(ocelot_phylink_mac_link_down); - --void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- phy_interface_t interface, -- struct phy_device *phy) -+void ocelot_port_enable(struct ocelot *ocelot, int port, -+ struct phy_device *phy) - { - /* Enable receiving frames on the port, and activate auto-learning of - * MAC addresses. -@@ -551,22 +499,62 @@ void ocelot_phylink_mac_link_up(struct o - ANA_PORT_PORT_CFG_PORTID_VAL(port), - ANA_PORT_PORT_CFG, port); - } --EXPORT_SYMBOL(ocelot_phylink_mac_link_up); -+EXPORT_SYMBOL(ocelot_port_enable); - - static int ocelot_port_open(struct net_device *dev) - { - struct ocelot_port_private *priv = netdev_priv(dev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ int err; -+ -+ if (priv->serdes) { -+ err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, -+ priv->phy_mode); -+ if (err) { -+ netdev_err(dev, "Could not set mode of SerDes\n"); -+ return err; -+ } -+ } -+ -+ err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, -+ priv->phy_mode); -+ if (err) { -+ netdev_err(dev, "Could not attach to PHY\n"); -+ return err; -+ } - -- phylink_start(priv->phylink); -+ dev->phydev = priv->phy; -+ -+ phy_attached_info(priv->phy); -+ phy_start(priv->phy); -+ -+ ocelot_port_enable(ocelot, port, priv->phy); - - return 0; - } - -+void ocelot_port_disable(struct ocelot *ocelot, int port) -+{ -+ struct ocelot_port *ocelot_port = ocelot->ports[port]; -+ -+ ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG); -+ ocelot_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, -+ QSYS_SWITCH_PORT_MODE, port); -+} -+EXPORT_SYMBOL(ocelot_port_disable); -+ - static int ocelot_port_stop(struct net_device *dev) - { - struct ocelot_port_private *priv = netdev_priv(dev); -+ struct ocelot *ocelot = priv->port.ocelot; -+ int port = priv->chip_port; -+ -+ phy_disconnect(priv->phy); -+ -+ dev->phydev = NULL; - -- phylink_stop(priv->phylink); -+ ocelot_port_disable(ocelot, port); - - return 0; - } -@@ -2263,7 +2251,8 @@ void ocelot_init_port(struct ocelot *oce - EXPORT_SYMBOL(ocelot_init_port); - - int ocelot_probe_port(struct ocelot *ocelot, u8 port, -- void __iomem *regs) -+ void __iomem *regs, -+ struct phy_device *phy) - { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; -@@ -2276,6 +2265,7 @@ int ocelot_probe_port(struct ocelot *oce - SET_NETDEV_DEV(dev, ocelot->dev); - priv = netdev_priv(dev); - priv->dev = dev; -+ priv->phy = phy; - priv->chip_port = port; - ocelot_port = &priv->port; - ocelot_port->ocelot = ocelot; ---- a/drivers/net/ethernet/mscc/ocelot.h -+++ b/drivers/net/ethernet/mscc/ocelot.h -@@ -12,7 +12,8 @@ - #include <linux/etherdevice.h> - #include <linux/if_vlan.h> - #include <linux/net_tstamp.h> --#include <linux/phylink.h> -+#include <linux/phy.h> -+#include <linux/phy/phy.h> - #include <linux/platform_device.h> - #include <linux/ptp_clock_kernel.h> - #include <linux/regmap.h> -@@ -64,12 +65,14 @@ struct ocelot_multicast { - struct ocelot_port_private { - struct ocelot_port port; - struct net_device *dev; -- struct phylink *phylink; -- struct phylink_config phylink_config; -+ struct phy_device *phy; - u8 chip_port; - - u8 vlan_aware; - -+ phy_interface_t phy_mode; -+ struct phy *serdes; -+ - struct ocelot_port_tc tc; - }; - -@@ -80,7 +83,9 @@ void ocelot_port_writel(struct ocelot_po - #define ocelot_field_read(ocelot, reg, val) regmap_field_read((ocelot)->regfields[(reg)], (val)) - - int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops); --int ocelot_probe_port(struct ocelot *ocelot, u8 port, void __iomem *regs); -+int ocelot_probe_port(struct ocelot *ocelot, u8 port, -+ void __iomem *regs, -+ struct phy_device *phy); - - void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, - enum ocelot_tag_prefix injection, ---- a/drivers/net/ethernet/mscc/ocelot_board.c -+++ b/drivers/net/ethernet/mscc/ocelot_board.c -@@ -13,7 +13,6 @@ - #include <linux/mfd/syscon.h> - #include <linux/skbuff.h> - #include <net/switchdev.h> --#include <linux/phy/phy.h> - - #include "ocelot.h" - -@@ -263,91 +262,6 @@ static const struct ocelot_ops ocelot_op - .reset = ocelot_reset, - }; - --static void ocelot_port_phylink_validate(struct phylink_config *config, -- unsigned long *supported, -- struct phylink_link_state *state) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- ocelot_phylink_validate(ocelot, port, supported, state); --} -- --static int --ocelot_port_phylink_mac_pcs_get_state(struct phylink_config *config, -- struct phylink_link_state *state) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- ocelot_phylink_mac_pcs_get_state(ocelot, port, state); -- -- return 0; --} -- --static void ocelot_port_phylink_mac_an_restart(struct phylink_config *config) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- ocelot_phylink_mac_an_restart(ocelot, port); --} -- --static void --ocelot_port_phylink_mac_config(struct phylink_config *config, -- unsigned int link_an_mode, -- const struct phylink_link_state *state) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- ocelot_phylink_mac_config(ocelot, port, link_an_mode, state); --} -- --static void ocelot_port_phylink_mac_link_down(struct phylink_config *config, -- unsigned int link_an_mode, -- phy_interface_t interface) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- return ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, -- interface); --} -- --static void ocelot_port_phylink_mac_link_up(struct phylink_config *config, -- unsigned int link_an_mode, -- phy_interface_t interface, -- struct phy_device *phy) --{ -- struct net_device *ndev = to_net_dev(config->dev); -- struct ocelot_port_private *priv = netdev_priv(ndev); -- struct ocelot *ocelot = priv->port.ocelot; -- int port = priv->chip_port; -- -- return ocelot_phylink_mac_link_up(ocelot, port, link_an_mode, -- interface, phy); --} -- --static const struct phylink_mac_ops ocelot_phylink_ops = { -- .validate = ocelot_port_phylink_validate, -- .mac_link_state = ocelot_port_phylink_mac_pcs_get_state, -- .mac_an_restart = ocelot_port_phylink_mac_an_restart, -- .mac_config = ocelot_port_phylink_mac_config, -- .mac_link_down = ocelot_port_phylink_mac_link_down, -- .mac_link_up = ocelot_port_phylink_mac_link_up, --}; -- - static int mscc_ocelot_probe(struct platform_device *pdev) - { - struct device_node *np = pdev->dev.of_node; -@@ -455,6 +369,8 @@ static int mscc_ocelot_probe(struct plat - for_each_available_child_of_node(ports, portnp) { - struct ocelot_port_private *priv; - struct ocelot_port *ocelot_port; -+ struct device_node *phy_node; -+ struct phy_device *phy; - struct resource *res; - struct phy *serdes; - void __iomem *regs; -@@ -473,7 +389,16 @@ static int mscc_ocelot_probe(struct plat - if (IS_ERR(regs)) - continue; - -- err = ocelot_probe_port(ocelot, port, regs); -+ phy_node = of_parse_phandle(portnp, "phy-handle", 0); -+ if (!phy_node) -+ continue; -+ -+ phy = of_phy_find_device(phy_node); -+ of_node_put(phy_node); -+ if (!phy) -+ continue; -+ -+ err = ocelot_probe_port(ocelot, port, regs, phy); - if (err) { - of_node_put(portnp); - goto out_put_ports; -@@ -487,7 +412,9 @@ static int mscc_ocelot_probe(struct plat - if (phy_mode < 0) - phy_mode = PHY_INTERFACE_MODE_NA; - -- switch (phy_mode) { -+ priv->phy_mode = phy_mode; -+ -+ switch (priv->phy_mode) { - case PHY_INTERFACE_MODE_NA: - continue; - case PHY_INTERFACE_MODE_SGMII: -@@ -524,41 +451,7 @@ static int mscc_ocelot_probe(struct plat - goto out_put_ports; - } - -- if (serdes) { -- err = phy_set_mode_ext(serdes, PHY_MODE_ETHERNET, -- phy_mode); -- if (err) { -- dev_err(ocelot->dev, -- "Could not set mode of SerDes\n"); -- of_node_put(portnp); -- goto out_put_ports; -- } -- } -- -- priv->phylink_config.dev = &priv->dev->dev; -- priv->phylink_config.type = PHYLINK_NETDEV; -- -- priv->phylink = phylink_create(&priv->phylink_config, -- of_fwnode_handle(portnp), -- phy_mode, &ocelot_phylink_ops); -- if (IS_ERR(priv->phylink)) { -- dev_err(ocelot->dev, -- "Could not create a phylink instance (%ld)\n", -- PTR_ERR(priv->phylink)); -- err = PTR_ERR(priv->phylink); -- priv->phylink = NULL; -- of_node_put(portnp); -- goto out_put_ports; -- } -- -- err = phylink_of_phy_connect(priv->phylink, portnp, 0); -- if (err) { -- dev_err(ocelot->dev, "Could not connect to PHY: %d\n", -- err); -- phylink_destroy(priv->phylink); -- of_node_put(portnp); -- goto out_put_ports; -- } -+ priv->serdes = serdes; - } - - register_netdevice_notifier(&ocelot_netdevice_nb); -@@ -575,27 +468,12 @@ out_put_ports: - static int mscc_ocelot_remove(struct platform_device *pdev) - { - struct ocelot *ocelot = platform_get_drvdata(pdev); -- int port; - - ocelot_deinit(ocelot); - unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb); - unregister_switchdev_notifier(&ocelot_switchdev_nb); - unregister_netdevice_notifier(&ocelot_netdevice_nb); - -- for (port = 0; port < ocelot->num_phys_ports; port++) { -- struct ocelot_port_private *priv; -- -- priv = container_of(ocelot->ports[port], -- struct ocelot_port_private, -- port); -- -- if (priv->phylink) { -- rtnl_lock(); -- phylink_destroy(priv->phylink); -- rtnl_unlock(); -- } -- } -- - return 0; - } - ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -518,12 +518,17 @@ void ocelot_deinit(struct ocelot *ocelot - void ocelot_init_port(struct ocelot *ocelot, int port); - - /* DSA callbacks */ -+void ocelot_port_enable(struct ocelot *ocelot, int port, -+ struct phy_device *phy); -+void ocelot_port_disable(struct ocelot *ocelot, int port); - void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data); - void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data); - int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset); - int ocelot_get_ts_info(struct ocelot *ocelot, int port, - struct ethtool_ts_info *info); - void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs); -+void ocelot_adjust_link(struct ocelot *ocelot, int port, -+ struct phy_device *phydev); - void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, - bool vlan_aware); - void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state); -@@ -592,21 +597,4 @@ int ocelot_rtag_parse_enable(struct ocel - int ocelot_dscp_set(struct ocelot *ocelot, int port, - bool enable, const u8 dscp_ix, - struct tsn_qos_switch_dscp_conf *c); --void ocelot_phylink_validate(struct ocelot *ocelot, int port, -- unsigned long *supported, -- struct phylink_link_state *state); --void ocelot_phylink_mac_pcs_get_state(struct ocelot *ocelot, int port, -- struct phylink_link_state *state); --void ocelot_phylink_mac_an_restart(struct ocelot *ocelot, int port); --void ocelot_phylink_mac_config(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- const struct phylink_link_state *state); --void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- phy_interface_t interface); --void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port, -- unsigned int link_an_mode, -- phy_interface_t interface, -- struct phy_device *phy); -- - #endif diff --git a/target/linux/layerscape/patches-5.4/701-net-0371-Revert-net-mscc-ocelot-do-not-force-Felix-MACs-at-lo.patch b/target/linux/layerscape/patches-5.4/701-net-0371-Revert-net-mscc-ocelot-do-not-force-Felix-MACs-at-lo.patch deleted file mode 100644 index 4859d17463..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0371-Revert-net-mscc-ocelot-do-not-force-Felix-MACs-at-lo.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 6864e50946bf92f96b8452d1e47765230a276bb0 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:30:58 +0200 -Subject: [PATCH] Revert "net: mscc: ocelot: do not force Felix MACs at lower - speeds than gigabit" - -This reverts commit f3ebad1269aad8a04710e84dc1cd5de485e5fec4. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/dsa/ocelot/felix.c | 1 - - drivers/net/dsa/ocelot/felix.h | 1 - - drivers/net/dsa/ocelot/felix_vsc9959.c | 1 - - drivers/net/ethernet/mscc/ocelot.c | 32 ++++++++++++++------------------ - include/soc/mscc/ocelot.h | 7 ------- - 5 files changed, 14 insertions(+), 28 deletions(-) - ---- a/drivers/net/dsa/ocelot/felix.c -+++ b/drivers/net/dsa/ocelot/felix.c -@@ -249,7 +249,6 @@ static int felix_init_structs(struct fel - ocelot->num_stats = felix->info->num_stats; - ocelot->shared_queue_sz = felix->info->shared_queue_sz; - ocelot->ops = felix->info->ops; -- ocelot->quirks = felix->info->quirks; - - base = pci_resource_start(felix->pdev, felix->info->pci_bar); - ---- a/drivers/net/dsa/ocelot/felix.h -+++ b/drivers/net/dsa/ocelot/felix.h -@@ -18,7 +18,6 @@ struct felix_info { - unsigned int num_stats; - int num_ports; - int pci_bar; -- unsigned long quirks; - }; - - extern struct felix_info felix_info_vsc9959; ---- a/drivers/net/dsa/ocelot/felix_vsc9959.c -+++ b/drivers/net/dsa/ocelot/felix_vsc9959.c -@@ -584,5 +584,4 @@ struct felix_info felix_info_vsc9959 = { - .shared_queue_sz = 128 * 1024, - .num_ports = 6, - .pci_bar = 4, -- .quirks = OCELOT_PCS_PERFORMS_RATE_ADAPTATION, - }; ---- a/drivers/net/ethernet/mscc/ocelot.c -+++ b/drivers/net/ethernet/mscc/ocelot.c -@@ -409,32 +409,27 @@ static u16 ocelot_wm_enc(u16 value) - void ocelot_adjust_link(struct ocelot *ocelot, int port, - struct phy_device *phydev) - { -- int speed, mac_speed, mac_mode = DEV_MAC_MODE_CFG_FDX_ENA; - struct ocelot_port *ocelot_port = ocelot->ports[port]; -+ int speed, mode = 0; - -- if (ocelot->quirks & OCELOT_PCS_PERFORMS_RATE_ADAPTATION) -- speed = SPEED_1000; -- else -- speed = phydev->speed; -- -- switch (speed) { -+ switch (phydev->speed) { - case SPEED_10: -- mac_speed = OCELOT_SPEED_10; -+ speed = OCELOT_SPEED_10; - break; - case SPEED_100: -- mac_speed = OCELOT_SPEED_100; -+ speed = OCELOT_SPEED_100; - break; - case SPEED_1000: -- mac_speed = OCELOT_SPEED_1000; -- mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; -+ speed = OCELOT_SPEED_1000; -+ mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - case SPEED_2500: -- mac_speed = OCELOT_SPEED_2500; -- mac_mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA; -+ speed = OCELOT_SPEED_2500; -+ mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; - break; - default: - dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n", -- port, speed); -+ port, phydev->speed); - return; - } - -@@ -444,7 +439,8 @@ void ocelot_adjust_link(struct ocelot *o - return; - - /* Only full duplex supported for now */ -- ocelot_port_writel(ocelot_port, mac_mode, DEV_MAC_MODE_CFG); -+ ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA | -+ mode, DEV_MAC_MODE_CFG); - - if (ocelot->ops->pcs_init) - ocelot->ops->pcs_init(ocelot, port); -@@ -455,11 +451,11 @@ void ocelot_adjust_link(struct ocelot *o - - /* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of - * reset */ -- ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(mac_speed), -+ ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed), - DEV_CLOCK_CFG); - - /* No PFC */ -- ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(mac_speed), -+ ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed), - ANA_PFC_PFC_CFG, port); - - /* Core: Enable port for frame transfer */ -@@ -473,7 +469,7 @@ void ocelot_adjust_link(struct ocelot *o - SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA | - SYS_MAC_FC_CFG_ZERO_PAUSE_ENA | - SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) | -- SYS_MAC_FC_CFG_FC_LINK_SPEED(mac_speed), -+ SYS_MAC_FC_CFG_FC_LINK_SPEED(speed), - SYS_MAC_FC_CFG, port); - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); - } ---- a/include/soc/mscc/ocelot.h -+++ b/include/soc/mscc/ocelot.h -@@ -404,11 +404,6 @@ enum ocelot_tag_prefix { - OCELOT_TAG_PREFIX_LONG, - }; - --/* Hardware quirks (differences between switch instantiations) */ --enum { -- OCELOT_PCS_PERFORMS_RATE_ADAPTATION = BIT(0), --}; -- - struct ocelot; - - struct ocelot_ops { -@@ -469,8 +464,6 @@ struct ocelot { - struct delayed_work stats_work; - struct workqueue_struct *stats_queue; - -- unsigned long quirks; -- - u8 ptp:1; - struct ptp_clock *ptp_clock; - struct ptp_clock_info ptp_info; diff --git a/target/linux/layerscape/patches-5.4/701-net-0372-Revert-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-va.patch b/target/linux/layerscape/patches-5.4/701-net-0372-Revert-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-va.patch deleted file mode 100644 index fda777ba99..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0372-Revert-enetc-Set-MDIO_CFG_HOLD-to-the-recommended-va.patch +++ /dev/null @@ -1,55 +0,0 @@ -From d93ca8d9c8365cf533f88582c57c83cb50fe598a Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:31:13 +0200 -Subject: [PATCH] Revert "enetc: Set MDIO_CFG_HOLD to the recommended value of - 2" - -This reverts commit 3f643e4af035886cd2ca4bf79967098cfe55b40b. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/freescale/enetc/enetc_mdio.c | 12 ++++-------- - 1 file changed, 4 insertions(+), 8 deletions(-) - ---- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -@@ -31,19 +31,15 @@ static inline void _enetc_mdio_wr(struct - _enetc_mdio_wr(mdio_priv, ENETC_##off, val) - #define enetc_mdio_rd_reg(off) enetc_mdio_rd(mdio_priv, off) - -+#define ENETC_MDC_DIV 258 -+ - #define MDIO_CFG_CLKDIV(x) ((((x) >> 1) & 0xff) << 8) - #define MDIO_CFG_BSY BIT(0) - #define MDIO_CFG_RD_ER BIT(1) --#define MDIO_CFG_HOLD(x) (((x) << 2) & GENMASK(4, 2)) - #define MDIO_CFG_ENC45 BIT(6) - /* external MDIO only - driven on neg MDC edge */ - #define MDIO_CFG_NEG BIT(23) - --#define ENETC_EMDIO_CFG \ -- (MDIO_CFG_HOLD(2) | \ -- MDIO_CFG_CLKDIV(258) | \ -- MDIO_CFG_NEG) -- - #define MDIO_CTL_DEV_ADDR(x) ((x) & 0x1f) - #define MDIO_CTL_PORT_ADDR(x) (((x) & 0x1f) << 5) - #define MDIO_CTL_READ BIT(15) -@@ -65,7 +61,7 @@ int enetc_mdio_write(struct mii_bus *bus - u16 dev_addr; - int ret; - -- mdio_cfg = ENETC_EMDIO_CFG; -+ mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; - if (regnum & MII_ADDR_C45) { - dev_addr = (regnum >> 16) & 0x1f; - mdio_cfg |= MDIO_CFG_ENC45; -@@ -112,7 +108,7 @@ int enetc_mdio_read(struct mii_bus *bus, - u16 dev_addr, value; - int ret; - -- mdio_cfg = ENETC_EMDIO_CFG; -+ mdio_cfg = MDIO_CFG_CLKDIV(ENETC_MDC_DIV) | MDIO_CFG_NEG; - if (regnum & MII_ADDR_C45) { - dev_addr = (regnum >> 16) & 0x1f; - mdio_cfg |= MDIO_CFG_ENC45; diff --git a/target/linux/layerscape/patches-5.4/701-net-0373-Revert-enetc-export-enetc_mdio-definitionns-to-inclu.patch b/target/linux/layerscape/patches-5.4/701-net-0373-Revert-enetc-export-enetc_mdio-definitionns-to-inclu.patch deleted file mode 100644 index 5e934c69d1..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0373-Revert-enetc-export-enetc_mdio-definitionns-to-inclu.patch +++ /dev/null @@ -1,192 +0,0 @@ -From 78378454e7978ea3b4128822880677e6efa9ddef Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:31:22 +0200 -Subject: [PATCH] Revert "enetc: export enetc_mdio definitionns to - include/linux/fsl" - -This reverts commit f8d80c003b76f397696bf887ebbd37a78781c050. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/ethernet/freescale/enetc/enetc_mdio.c | 18 +--------- - drivers/net/ethernet/freescale/enetc/enetc_mdio.h | 12 +++++++ - .../net/ethernet/freescale/enetc/enetc_pci_mdio.c | 41 +++++++++------------- - include/linux/fsl/enetc_mdio.h | 21 ----------- - 4 files changed, 30 insertions(+), 62 deletions(-) - create mode 100644 drivers/net/ethernet/freescale/enetc/enetc_mdio.h - delete mode 100644 include/linux/fsl/enetc_mdio.h - ---- a/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.c -@@ -1,13 +1,13 @@ - // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) - /* Copyright 2019 NXP */ - --#include <linux/fsl/enetc_mdio.h> - #include <linux/mdio.h> - #include <linux/of_mdio.h> - #include <linux/iopoll.h> - #include <linux/of.h> - - #include "enetc_pf.h" -+#include "enetc_mdio.h" - - #define ENETC_MDIO_CFG 0x0 /* MDIO configuration and status */ - #define ENETC_MDIO_CTL 0x4 /* MDIO control */ -@@ -99,7 +99,6 @@ int enetc_mdio_write(struct mii_bus *bus - - return 0; - } --EXPORT_SYMBOL_GPL(enetc_mdio_write); - - int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum) - { -@@ -155,21 +154,6 @@ int enetc_mdio_read(struct mii_bus *bus, - - return value; - } --EXPORT_SYMBOL_GPL(enetc_mdio_read); -- --struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs) --{ -- struct enetc_hw *hw; -- -- hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); -- if (!hw) -- return ERR_PTR(-ENOMEM); -- -- hw->port = port_regs; -- -- return hw; --} --EXPORT_SYMBOL_GPL(enetc_hw_alloc); - - int enetc_mdio_probe(struct enetc_pf *pf) - { ---- /dev/null -+++ b/drivers/net/ethernet/freescale/enetc/enetc_mdio.h -@@ -0,0 +1,12 @@ -+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ -+/* Copyright 2019 NXP */ -+ -+#include <linux/phy.h> -+ -+struct enetc_mdio_priv { -+ struct enetc_hw *hw; -+ int mdio_base; -+}; -+ -+int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value); -+int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum); ---- a/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c -+++ b/drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c -@@ -1,8 +1,8 @@ - // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) - /* Copyright 2019 NXP */ --#include <linux/fsl/enetc_mdio.h> - #include <linux/of_mdio.h> - #include "enetc_pf.h" -+#include "enetc_mdio.h" - - #define ENETC_MDIO_DEV_ID 0xee01 - #define ENETC_MDIO_DEV_NAME "FSL PCIe IE Central MDIO" -@@ -14,29 +14,17 @@ static int enetc_pci_mdio_probe(struct p - { - struct enetc_mdio_priv *mdio_priv; - struct device *dev = &pdev->dev; -- void __iomem *port_regs; - struct enetc_hw *hw; - struct mii_bus *bus; - int err; - -- port_regs = pci_iomap(pdev, 0, 0); -- if (!port_regs) { -- dev_err(dev, "iomap failed\n"); -- err = -ENXIO; -- goto err_ioremap; -- } -- -- hw = enetc_hw_alloc(dev, port_regs); -- if (IS_ERR(enetc_hw_alloc)) { -- err = PTR_ERR(hw); -- goto err_hw_alloc; -- } -+ hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); -+ if (!hw) -+ return -ENOMEM; - - bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv)); -- if (!bus) { -- err = -ENOMEM; -- goto err_mdiobus_alloc; -- } -+ if (!bus) -+ return -ENOMEM; - - bus->name = ENETC_MDIO_BUS_NAME; - bus->read = enetc_mdio_read; -@@ -51,7 +39,7 @@ static int enetc_pci_mdio_probe(struct p - err = pci_enable_device_mem(pdev); - if (err) { - dev_err(dev, "device enable failed\n"); -- goto err_pci_enable; -+ return err; - } - - err = pci_request_region(pdev, 0, KBUILD_MODNAME); -@@ -60,6 +48,13 @@ static int enetc_pci_mdio_probe(struct p - goto err_pci_mem_reg; - } - -+ hw->port = pci_iomap(pdev, 0, 0); -+ if (!hw->port) { -+ err = -ENXIO; -+ dev_err(dev, "iomap failed\n"); -+ goto err_ioremap; -+ } -+ - err = of_mdiobus_register(bus, dev->of_node); - if (err) - goto err_mdiobus_reg; -@@ -69,14 +64,12 @@ static int enetc_pci_mdio_probe(struct p - return 0; - - err_mdiobus_reg: -+ iounmap(mdio_priv->hw->port); -+err_ioremap: - pci_release_mem_regions(pdev); - err_pci_mem_reg: - pci_disable_device(pdev); --err_pci_enable: --err_mdiobus_alloc: -- iounmap(port_regs); --err_hw_alloc: --err_ioremap: -+ - return err; - } - ---- a/include/linux/fsl/enetc_mdio.h -+++ /dev/null -@@ -1,21 +0,0 @@ --/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ --/* Copyright 2019 NXP */ -- --#include <linux/phy.h> -- --/* PCS registers */ --#define ENETC_PCS_LINK_TIMER1 0x12 --#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0 --#define ENETC_PCS_LINK_TIMER2 0x13 --#define ENETC_PCS_LINK_TIMER2_VAL 0x0003 --#define ENETC_PCS_IF_MODE 0x14 --#define ENETC_PCS_IF_MODE_SGMII_AN 0x0003 -- --struct enetc_mdio_priv { -- struct enetc_hw *hw; -- int mdio_base; --}; -- --int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value); --int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum); --struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs); diff --git a/target/linux/layerscape/patches-5.4/701-net-0374-Revert-net-phylink-call-mac_an_restart-for-SGMII-QSG.patch b/target/linux/layerscape/patches-5.4/701-net-0374-Revert-net-phylink-call-mac_an_restart-for-SGMII-QSG.patch deleted file mode 100644 index 4b2cee677a..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0374-Revert-net-phylink-call-mac_an_restart-for-SGMII-QSG.patch +++ /dev/null @@ -1,26 +0,0 @@ -From d3a532021976d98bfc9e3dac18a17abccf6eb567 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:31:42 +0200 -Subject: [PATCH] Revert "net: phylink: call mac_an_restart for SGMII/QSGMII - inband interfaces too" - -This reverts commit 57fe15baac62dad00817e3359cefc123e24f10a2. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/phy/phylink.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -360,9 +360,7 @@ static void phylink_mac_config_up(struct - static void phylink_mac_an_restart(struct phylink *pl) - { - if (pl->link_config.an_enabled && -- (phy_interface_mode_is_8023z(pl->link_config.interface) || -- pl->link_config.interface == PHY_INTERFACE_MODE_SGMII || -- pl->link_config.interface == PHY_INTERFACE_MODE_QSGMII)) -+ phy_interface_mode_is_8023z(pl->link_config.interface)) - pl->ops->mac_an_restart(pl->config); - } - diff --git a/target/linux/layerscape/patches-5.4/701-net-0375-Revert-net-phylink-make-QSGMII-a-valid-PHY-mode-for-.patch b/target/linux/layerscape/patches-5.4/701-net-0375-Revert-net-phylink-make-QSGMII-a-valid-PHY-mode-for-.patch deleted file mode 100644 index e2bce2f6a1..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0375-Revert-net-phylink-make-QSGMII-a-valid-PHY-mode-for-.patch +++ /dev/null @@ -1,23 +0,0 @@ -From e53b64cab45ea4d01226840efd963a6bec8ad2f9 Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:31:58 +0200 -Subject: [PATCH] Revert "net: phylink: make QSGMII a valid PHY mode for - in-band AN" - -This reverts commit fe7fd9f6afce50c0fc09fd3d56cd59eda44d8dd0. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - drivers/net/phy/phylink.c | 1 - - 1 file changed, 1 deletion(-) - ---- a/drivers/net/phy/phylink.c -+++ b/drivers/net/phy/phylink.c -@@ -283,7 +283,6 @@ static int phylink_parse_mode(struct phy - - switch (pl->link_config.interface) { - case PHY_INTERFACE_MODE_SGMII: -- case PHY_INTERFACE_MODE_QSGMII: - phylink_set(pl->supported, 10baseT_Half); - phylink_set(pl->supported, 10baseT_Full); - phylink_set(pl->supported, 100baseT_Half); diff --git a/target/linux/layerscape/patches-5.4/701-net-0376-Revert-mii-Add-helpers-for-parsing-SGMII-auto-negoti.patch b/target/linux/layerscape/patches-5.4/701-net-0376-Revert-mii-Add-helpers-for-parsing-SGMII-auto-negoti.patch deleted file mode 100644 index 3831cac482..0000000000 --- a/target/linux/layerscape/patches-5.4/701-net-0376-Revert-mii-Add-helpers-for-parsing-SGMII-auto-negoti.patch +++ /dev/null @@ -1,110 +0,0 @@ -From c6f9fb78b7534392d3be307e566d10c8525c7c9a Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Mon, 6 Jan 2020 14:32:06 +0200 -Subject: [PATCH] Revert "mii: Add helpers for parsing SGMII auto-negotiation" - -This reverts commit de81e3c1ccbf27eda7584e23c713705a221a57da. - -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> ---- - include/linux/mii.h | 50 ------------------------------------------------ - include/uapi/linux/mii.h | 10 ---------- - 2 files changed, 60 deletions(-) - ---- a/include/linux/mii.h -+++ b/include/linux/mii.h -@@ -373,56 +373,6 @@ static inline u32 mii_lpa_to_ethtool_lpa - } - - /** -- * mii_lpa_mod_linkmode_adv_sgmii -- * @lp_advertising: pointer to destination link mode. -- * @lpa: value of the MII_LPA register -- * -- * A small helper function that translates MII_LPA bits to -- * linkmode advertisement settings for SGMII. -- * Leaves other bits unchanged. -- */ --static inline void --mii_lpa_mod_linkmode_lpa_sgmii(unsigned long *lp_advertising, u32 lpa) --{ -- u32 speed_duplex = lpa & LPA_SGMII_DPX_SPD_MASK; -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_1000HALF); -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_1000FULL); -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_100HALF); -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_100FULL); -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_10HALF); -- -- linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, lp_advertising, -- speed_duplex == LPA_SGMII_10FULL); --} -- --/** -- * mii_lpa_to_linkmode_adv_sgmii -- * @advertising: pointer to destination link mode. -- * @lpa: value of the MII_LPA register -- * -- * A small helper function that translates MII_ADVERTISE bits -- * to linkmode advertisement settings when in SGMII mode. -- * Clears the old value of advertising. -- */ --static inline void mii_lpa_to_linkmode_lpa_sgmii(unsigned long *lp_advertising, -- u32 lpa) --{ -- linkmode_zero(lp_advertising); -- -- mii_lpa_mod_linkmode_lpa_sgmii(lp_advertising, lpa); --} -- --/** - * mii_adv_mod_linkmode_adv_t - * @advertising:pointer to destination link mode. - * @adv: value of the MII_ADVERTISE register ---- a/include/uapi/linux/mii.h -+++ b/include/uapi/linux/mii.h -@@ -71,7 +71,6 @@ - /* Advertisement control register. */ - #define ADVERTISE_SLCT 0x001f /* Selector bits */ - #define ADVERTISE_CSMA 0x0001 /* Only selector supported */ --#define ADVERTISE_SGMII 0x0001 /* Can do SGMII */ - #define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ - #define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ - #define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -@@ -95,7 +94,6 @@ - - /* Link partner ability register. */ - #define LPA_SLCT 0x001f /* Same as advertise selector */ --#define LPA_SGMII 0x0001 /* Can do SGMII */ - #define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ - #define LPA_1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ - #define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ -@@ -106,19 +104,11 @@ - #define LPA_1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/ - #define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ - #define LPA_PAUSE_CAP 0x0400 /* Can pause */ --#define LPA_SGMII_DPX_SPD_MASK 0x1C00 /* SGMII duplex and speed bits */ --#define LPA_SGMII_10HALF 0x0000 /* Can do SGMII 10mbps half-duplex */ --#define LPA_SGMII_10FULL 0x1000 /* Can do SGMII 10mbps full-duplex */ --#define LPA_SGMII_100HALF 0x0400 /* Can do SGMII 100mbps half-duplex */ --#define LPA_SGMII_100FULL 0x1400 /* Can do SGMII 100mbps full-duplex */ - #define LPA_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ --#define LPA_SGMII_1000HALF 0x0800 /* Can do SGMII 1000mbps half-duplex */ --#define LPA_SGMII_1000FULL 0x1800 /* Can do SGMII 1000mbps full-duplex */ - #define LPA_RESV 0x1000 /* Unused... */ - #define LPA_RFAULT 0x2000 /* Link partner faulted */ - #define LPA_LPACK 0x4000 /* Link partner acked us */ - #define LPA_NPAGE 0x8000 /* Next page bit */ --#define LPA_SGMII_LINK 0x8000 /* Link partner has link */ - - #define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) - #define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) diff --git a/target/linux/layerscape/patches-5.4/801-audio-0023-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-part-2.patch b/target/linux/layerscape/patches-5.4/801-audio-0023-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-part-2.patch deleted file mode 100644 index d09dd9b415..0000000000 --- a/target/linux/layerscape/patches-5.4/801-audio-0023-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-part-2.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 5d816f1a5cac458a7fff9ecf896c85eb351a7352 Mon Sep 17 00:00:00 2001 -From: Viorel Suman <viorel.suman@nxp.com> -Date: Mon, 30 Mar 2020 16:24:59 +0800 -Subject: [PATCH] ASoC: fsl: add imx-pcm-dma v2 platform driver (part 2) - -which don't request the dma channel in the probe, but request -dma channel when needed. for the dma channel of cpu dai in BE -can be reused by the FE. - -Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> -[ Aisheng: split PCM changes ] -Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> -[rebase] -Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> ---- - sound/soc/fsl/fsl_sai.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - ---- a/sound/soc/fsl/fsl_sai.c -+++ b/sound/soc/fsl/fsl_sai.c -@@ -1046,6 +1046,8 @@ static int fsl_sai_probe(struct platform - MCLK_DIR(index)); - } - -+ sai->dma_params_rx.filter_data = "rx"; -+ sai->dma_params_tx.filter_data = "tx"; - sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0; - sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0; - sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; -@@ -1061,7 +1063,7 @@ static int fsl_sai_probe(struct platform - goto err_pm_disable; - - if (sai->soc->imx) -- ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE); -+ ret = imx_pcm_platform_register(&pdev->dev); - if (ret) - goto err_pm_disable; - } else { diff --git a/target/linux/layerscape/patches-5.4/801-audio-0027-MLK-15140-1-ASoC-fsl_sai-support-latest-sai-module.patch b/target/linux/layerscape/patches-5.4/801-audio-0027-MLK-15140-1-ASoC-fsl_sai-support-latest-sai-module.patch index 38d3041bae..7f234a27cc 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0027-MLK-15140-1-ASoC-fsl_sai-support-latest-sai-module.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0027-MLK-15140-1-ASoC-fsl_sai-support-latest-sai-module.patch @@ -590,7 +590,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com> if (ret) { dev_err(&pdev->dev, "failed to claim irq %u\n", irq); return ret; -@@ -1090,6 +1134,7 @@ static int fsl_sai_runtime_suspend(struc +@@ -1088,6 +1132,7 @@ static int fsl_sai_runtime_suspend(struc static int fsl_sai_runtime_resume(struct device *dev) { struct fsl_sai *sai = dev_get_drvdata(dev); @@ -598,7 +598,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com> int ret; ret = clk_prepare_enable(sai->bus_clk); -@@ -1115,11 +1160,11 @@ static int fsl_sai_runtime_resume(struct +@@ -1113,11 +1158,11 @@ static int fsl_sai_runtime_resume(struct PM_QOS_CPU_DMA_LATENCY, 0); regcache_cache_only(sai->regmap, false); diff --git a/target/linux/layerscape/patches-5.4/801-audio-0030-MLK-15960-2-ASoC-fsl_sai-refine-the-pm-runtime-funct.patch b/target/linux/layerscape/patches-5.4/801-audio-0030-MLK-15960-2-ASoC-fsl_sai-refine-the-pm-runtime-funct.patch index 5232a69894..9f9db86f64 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0030-MLK-15960-2-ASoC-fsl_sai-refine-the-pm-runtime-funct.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0030-MLK-15960-2-ASoC-fsl_sai-refine-the-pm-runtime-funct.patch @@ -16,7 +16,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com> --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c -@@ -1096,6 +1096,8 @@ static int fsl_sai_probe(struct platform +@@ -1094,6 +1094,8 @@ static int fsl_sai_probe(struct platform pm_runtime_enable(&pdev->dev); @@ -25,7 +25,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com> ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, &fsl_sai_dai, 1); if (ret) -@@ -1139,6 +1141,8 @@ static int fsl_sai_runtime_suspend(struc +@@ -1137,6 +1139,8 @@ static int fsl_sai_runtime_suspend(struc { struct fsl_sai *sai = dev_get_drvdata(dev); diff --git a/target/linux/layerscape/patches-5.4/801-audio-0036-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_data-pa.patch b/target/linux/layerscape/patches-5.4/801-audio-0036-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_data-pa.patch deleted file mode 100644 index 5cc427496a..0000000000 --- a/target/linux/layerscape/patches-5.4/801-audio-0036-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_data-pa.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 8d4837f146cd14cf7041050ddec77b2a5c3f2f27 Mon Sep 17 00:00:00 2001 -From: Shengjiu Wang <shengjiu.wang@nxp.com> -Date: Tue, 23 Jan 2018 13:25:40 +0800 -Subject: [PATCH] MLK-17442: ASoC: fsl: fix wrong usage of filter_data (part 1) - -The filter_data should be used for dma_filter_fn function, -but we used the filter_data wrongly for dma channel name. -This patch is to fix the issue. - -Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> -Reviwed-by: Daniel Baluta <daniel.baluta@nxp.com> -[ Aisheng: split out esai and pcm changes ] -Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> ---- - sound/soc/fsl/fsl_sai.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/sound/soc/fsl/fsl_sai.c -+++ b/sound/soc/fsl/fsl_sai.c -@@ -1136,8 +1136,8 @@ static int fsl_sai_probe(struct platform - MCLK_DIR(index)); - } - -- sai->dma_params_rx.filter_data = "rx"; -- sai->dma_params_tx.filter_data = "tx"; -+ sai->dma_params_rx.chan_name = "rx"; -+ sai->dma_params_tx.chan_name = "tx"; - sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0; - sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0; - sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; diff --git a/target/linux/layerscape/patches-5.4/801-audio-0037-MLK-16224-4-ASoC-fsl_sai-support-multi-fifo-and-DSD.patch b/target/linux/layerscape/patches-5.4/801-audio-0037-MLK-16224-4-ASoC-fsl_sai-support-multi-fifo-and-DSD.patch index 033ab0d06b..b5802d3250 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0037-MLK-16224-4-ASoC-fsl_sai-support-multi-fifo-and-DSD.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0037-MLK-16224-4-ASoC-fsl_sai-support-multi-fifo-and-DSD.patch @@ -274,7 +274,7 @@ Reviewed-by: Viorel Suman <viorel.suman@nxp.com> if ((of_find_property(np, "fsl,i2s-xtor", NULL) != NULL) || (of_find_property(np, "fsl,txm-rxs", NULL) != NULL)) { -@@ -1143,6 +1282,11 @@ static int fsl_sai_probe(struct platform +@@ -1141,6 +1280,11 @@ static int fsl_sai_probe(struct platform sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; diff --git a/target/linux/layerscape/patches-5.4/801-audio-0048-MLK-17580-ASoC-fsl-sai-Use-DSD-helper.patch b/target/linux/layerscape/patches-5.4/801-audio-0048-MLK-17580-ASoC-fsl-sai-Use-DSD-helper.patch index ffd74f81d1..856f2bb6d7 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0048-MLK-17580-ASoC-fsl-sai-Use-DSD-helper.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0048-MLK-17580-ASoC-fsl-sai-Use-DSD-helper.patch @@ -73,7 +73,7 @@ Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com if (ret) { dev_err(cpu_dai->dev, "failed to set proper pins state: %d\n", ret); -@@ -1356,9 +1347,6 @@ static int fsl_sai_probe(struct platform +@@ -1354,9 +1345,6 @@ static int fsl_sai_probe(struct platform sai->pinctrl = devm_pinctrl_get(&pdev->dev); diff --git a/target/linux/layerscape/patches-5.4/801-audio-0049-MLK-17580-ASoC-fsl-sai-check-for-pinctrl-status.patch b/target/linux/layerscape/patches-5.4/801-audio-0049-MLK-17580-ASoC-fsl-sai-check-for-pinctrl-status.patch index 5f69c1daae..ebd71672b2 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0049-MLK-17580-ASoC-fsl-sai-check-for-pinctrl-status.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0049-MLK-17580-ASoC-fsl-sai-check-for-pinctrl-status.patch @@ -49,7 +49,7 @@ Signed-off-by: Viorel Suman <viorel.suman@nxp.com> ret = pinctrl_select_state(sai->pinctrl, sai->pins_state); if (ret) { dev_err(cpu_dai->dev, -@@ -1345,7 +1348,7 @@ static int fsl_sai_probe(struct platform +@@ -1343,7 +1346,7 @@ static int fsl_sai_probe(struct platform sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; diff --git a/target/linux/layerscape/patches-5.4/801-audio-0062-ASoC-fsl-sai-fix-build-failture-due-to-5.1-RC7-upgra.patch b/target/linux/layerscape/patches-5.4/801-audio-0062-ASoC-fsl-sai-fix-build-failture-due-to-5.1-RC7-upgra.patch index 7cce9d45a0..3f92c2185c 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0062-ASoC-fsl-sai-fix-build-failture-due-to-5.1-RC7-upgra.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0062-ASoC-fsl-sai-fix-build-failture-due-to-5.1-RC7-upgra.patch @@ -25,7 +25,7 @@ Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> break; case SND_SOC_DAIFMT_CBM_CFS: val_cr4 |= FSL_SAI_CR4_FSD_MSTR; -@@ -1574,14 +1572,6 @@ static int fsl_sai_remove(struct platfor +@@ -1572,14 +1570,6 @@ static int fsl_sai_remove(struct platfor return 0; } diff --git a/target/linux/layerscape/patches-5.4/801-audio-0064-ASoC-fsl_sai-Mark-cache-dirty-at-resume.patch b/target/linux/layerscape/patches-5.4/801-audio-0064-ASoC-fsl_sai-Mark-cache-dirty-at-resume.patch index 1f74ff235a..3fd3554985 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0064-ASoC-fsl_sai-Mark-cache-dirty-at-resume.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0064-ASoC-fsl_sai-Mark-cache-dirty-at-resume.patch @@ -17,7 +17,7 @@ Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com> --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c -@@ -1616,6 +1616,8 @@ static int fsl_sai_runtime_resume(struct +@@ -1614,6 +1614,8 @@ static int fsl_sai_runtime_resume(struct PM_QOS_CPU_DMA_LATENCY, 0); regcache_cache_only(sai->regmap, false); diff --git a/target/linux/layerscape/patches-5.4/801-audio-0065-LF-106-ASoC-fsl_sai-request-BUS_FREQ_AUDIO.patch b/target/linux/layerscape/patches-5.4/801-audio-0065-LF-106-ASoC-fsl_sai-request-BUS_FREQ_AUDIO.patch index 25c65f17dd..ed0afdbac8 100644 --- a/target/linux/layerscape/patches-5.4/801-audio-0065-LF-106-ASoC-fsl_sai-request-BUS_FREQ_AUDIO.patch +++ b/target/linux/layerscape/patches-5.4/801-audio-0065-LF-106-ASoC-fsl_sai-request-BUS_FREQ_AUDIO.patch @@ -20,7 +20,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> #include "fsl_dsd.h" #include "fsl_sai.h" -@@ -1570,6 +1571,8 @@ static int fsl_sai_runtime_suspend(struc +@@ -1568,6 +1569,8 @@ static int fsl_sai_runtime_suspend(struc regcache_cache_only(sai->regmap, true); @@ -29,7 +29,7 @@ Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com> if (sai->mclk_streams & BIT(SNDRV_PCM_STREAM_CAPTURE)) clk_disable_unprepare(sai->mclk_clk[sai->mclk_id[0]]); -@@ -1611,6 +1614,8 @@ static int fsl_sai_runtime_resume(struct +@@ -1609,6 +1612,8 @@ static int fsl_sai_runtime_resume(struct goto disable_tx_clk; } diff --git a/target/linux/layerscape/patches-5.4/801-audio-0067-Revert-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_.patch b/target/linux/layerscape/patches-5.4/801-audio-0067-Revert-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_.patch deleted file mode 100644 index d36101e09a..0000000000 --- a/target/linux/layerscape/patches-5.4/801-audio-0067-Revert-MLK-17442-ASoC-fsl-fix-wrong-usage-of-filter_.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 912323e5d1290cbee651270f65de338663012c9b Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 30 Mar 2020 16:30:03 +0800 -Subject: [PATCH] Revert "MLK-17442: ASoC: fsl: fix wrong usage of filter_data - (part 1)" - -This reverts commit 8d4837f146cd14cf7041050ddec77b2a5c3f2f27. ---- - sound/soc/fsl/fsl_sai.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- a/sound/soc/fsl/fsl_sai.c -+++ b/sound/soc/fsl/fsl_sai.c -@@ -1519,8 +1519,8 @@ static int fsl_sai_probe(struct platform - MCLK_DIR(index)); - } - -- sai->dma_params_rx.chan_name = "rx"; -- sai->dma_params_tx.chan_name = "tx"; -+ sai->dma_params_rx.filter_data = "rx"; -+ sai->dma_params_tx.filter_data = "tx"; - sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0; - sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0; - sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; diff --git a/target/linux/layerscape/patches-5.4/801-audio-0068-Revert-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-p.patch b/target/linux/layerscape/patches-5.4/801-audio-0068-Revert-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-p.patch deleted file mode 100644 index 1045b90494..0000000000 --- a/target/linux/layerscape/patches-5.4/801-audio-0068-Revert-ASoC-fsl-add-imx-pcm-dma-v2-platform-driver-p.patch +++ /dev/null @@ -1,31 +0,0 @@ -From d8715f02b84c1a210b6fd84badc41eced6272424 Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 30 Mar 2020 16:30:26 +0800 -Subject: [PATCH] Revert "ASoC: fsl: add imx-pcm-dma v2 platform driver (part - 2)" - -This reverts commit 5d816f1a5cac458a7fff9ecf896c85eb351a7352. ---- - sound/soc/fsl/fsl_sai.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - ---- a/sound/soc/fsl/fsl_sai.c -+++ b/sound/soc/fsl/fsl_sai.c -@@ -1519,8 +1519,6 @@ static int fsl_sai_probe(struct platform - MCLK_DIR(index)); - } - -- sai->dma_params_rx.filter_data = "rx"; -- sai->dma_params_tx.filter_data = "tx"; - sai->dma_params_rx.addr = res->start + FSL_SAI_RDR0; - sai->dma_params_tx.addr = res->start + FSL_SAI_TDR0; - sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; -@@ -1540,7 +1538,7 @@ static int fsl_sai_probe(struct platform - goto err_pm_disable; - - if (sai->soc->imx) -- ret = imx_pcm_platform_register(&pdev->dev); -+ ret = imx_pcm_dma_init(pdev, IMX_SAI_DMABUF_SIZE); - if (ret) - goto err_pm_disable; - } else { diff --git a/target/linux/layerscape/patches-5.4/805-display-0030-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sinks.patch b/target/linux/layerscape/patches-5.4/805-display-0030-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sinks.patch deleted file mode 100644 index be72d1fd38..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0030-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sinks.patch +++ /dev/null @@ -1,60 +0,0 @@ -From d9440f7cb8fa5853b9dcc6e2b165725ac3e8b70c Mon Sep 17 00:00:00 2001 -From: Laurentiu Palcu <laurentiu.palcu@nxp.com> -Date: Wed, 27 Nov 2019 13:34:51 +0200 -Subject: [PATCH] drm/imx/hdp: fix issue with non-SCDC HDMI sinks - -Currently, if sink does not support SCDC, even if the sink is HDMI 1.4 or 2.0, -the hdmi_type is left to default value (MODE_DVI). Hence the HDMI controler is -not properly initialized when cdns_hdmi_ctrl_init() is called. - -Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com> -Reported-by: Jared Hu <jared.hu@nxp.com> -Reviewed-by: Sandor Yu <sandor.yu@nxp.com> ---- - drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - ---- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c -+++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c -@@ -29,11 +29,17 @@ - static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) - { - struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; -- u8 buff; -+ struct drm_display_info *di = &mhdp->connector.base.display_info; -+ u8 buff = 0; -+ -+ if (scdc->supported || di->color_formats & DRM_COLOR_FORMAT_YCRCB420) -+ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; -+ else -+ mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; - - /* check sink support SCDC or not */ -- if (scdc->supported != true) { -- DRM_INFO("Sink Not Support SCDC\n"); -+ if (!scdc->supported) { -+ DRM_INFO("Sink does not support SCDC\n"); - return; - } - -@@ -43,19 +49,13 @@ static void hdmi_sink_config(struct cdns - * Enable scrambling and TMDS_Bit_Clock_Ratio - */ - buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE; -- mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; - } else if (scdc->scrambling.low_rates) { - /* - * Enable scrambling and HDMI2.0 when scrambling capability of sink - * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit - */ - buff = SCDC_SCRAMBLING_ENABLE; -- mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; -- } else { -- /* Default work in HDMI1.4 */ -- buff = 0; -- mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; -- } -+ } - - /* TMDS config */ - cdns_hdmi_scdc_write(mhdp, 0x20, buff); diff --git a/target/linux/layerscape/patches-5.4/805-display-0031-Revert-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sink.patch b/target/linux/layerscape/patches-5.4/805-display-0031-Revert-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sink.patch deleted file mode 100644 index e906be4604..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0031-Revert-drm-imx-hdp-fix-issue-with-non-SCDC-HDMI-sink.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 575fc16e92287349f0ffd8399dd9d7e408954cd4 Mon Sep 17 00:00:00 2001 -From: Sandor Yu <Sandor.yu@nxp.com> -Date: Fri, 29 Nov 2019 15:05:03 +0800 -Subject: [PATCH] Revert "drm/imx/hdp: fix issue with non-SCDC HDMI sinks" - -For HDMI sinks that support HDMI2.0, those video modes have -listed in hdmi1.4 specification should work in hdmi 1.4. -Remove the patch, make sure all video modes can work well -in HDMI2.0 sinks. -For non-SCDC HDMI sinks issue, -it will be fixed with another patch. - -This reverts commit 4b6617643f9e3a6f61d42eae39034ddc8d4825af. - -Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> ---- - drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - ---- a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c -+++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c -@@ -29,17 +29,11 @@ - static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) - { - struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc; -- struct drm_display_info *di = &mhdp->connector.base.display_info; -- u8 buff = 0; -- -- if (scdc->supported || di->color_formats & DRM_COLOR_FORMAT_YCRCB420) -- mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; -- else -- mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; -+ u8 buff; - - /* check sink support SCDC or not */ -- if (!scdc->supported) { -- DRM_INFO("Sink does not support SCDC\n"); -+ if (scdc->supported != true) { -+ DRM_INFO("Sink Not Support SCDC\n"); - return; - } - -@@ -49,13 +43,19 @@ static void hdmi_sink_config(struct cdns - * Enable scrambling and TMDS_Bit_Clock_Ratio - */ - buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE; -+ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; - } else if (scdc->scrambling.low_rates) { - /* - * Enable scrambling and HDMI2.0 when scrambling capability of sink - * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit - */ - buff = SCDC_SCRAMBLING_ENABLE; -- } -+ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; -+ } else { -+ /* Default work in HDMI1.4 */ -+ buff = 0; -+ mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; -+ } - - /* TMDS config */ - cdns_hdmi_scdc_write(mhdp, 0x20, buff); diff --git a/target/linux/layerscape/patches-5.4/805-display-0042-drm-imx-Revert-a-patch-which-merges-imx-drm-core-and.patch b/target/linux/layerscape/patches-5.4/805-display-0042-drm-imx-Revert-a-patch-which-merges-imx-drm-core-and.patch deleted file mode 100644 index 017f0e347c..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0042-drm-imx-Revert-a-patch-which-merges-imx-drm-core-and.patch +++ /dev/null @@ -1,116 +0,0 @@ -From d27d7c25c946776c015a39f1a9eb0c6d4e724a70 Mon Sep 17 00:00:00 2001 -From: Liu Ying <victor.liu@nxp.com> -Date: Fri, 25 Jan 2019 11:23:39 +0800 -Subject: [PATCH] drm/imx: Revert a patch which merges imx-drm-core and - ipuv3-crtc in one module - -DPU CRTC found in i.MX8qm/qxp SoCs can be hooked into imx-drm. -Thus, move ipuv3-crtc out of imx-drm-core. - -Revert "drm/imx: merge imx-drm-core and ipuv3-crtc in one module" - -This reverts commit 3d1df96ad46856ce850be5ac112eab919cbe1cab. - -Signed-off-by: Liu Ying <victor.liu@nxp.com> -[ Aisheng: fix conflicts ] -Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> ---- - drivers/gpu/drm/imx/Kconfig | 7 +++++++ - drivers/gpu/drm/imx/Makefile | 4 +++- - drivers/gpu/drm/imx/imx-drm-core.c | 18 +----------------- - drivers/gpu/drm/imx/imx-drm.h | 2 -- - drivers/gpu/drm/imx/ipuv3-crtc.c | 8 +++++++- - 5 files changed, 18 insertions(+), 21 deletions(-) - ---- a/drivers/gpu/drm/imx/Kconfig -+++ b/drivers/gpu/drm/imx/Kconfig -@@ -33,6 +33,13 @@ config DRM_IMX_LDB - Choose this to enable the internal LVDS Display Bridge (LDB) - found on i.MX53 and i.MX6 processors. - -+config DRM_IMX_IPUV3 -+ tristate -+ depends on DRM_IMX -+ depends on IMX_IPUV3_CORE -+ default y if DRM_IMX=y -+ default m if DRM_IMX=m -+ - config DRM_IMX_HDMI - tristate "Freescale i.MX DRM HDMI" - select DRM_DW_HDMI ---- a/drivers/gpu/drm/imx/Makefile -+++ b/drivers/gpu/drm/imx/Makefile -@@ -1,6 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0 - --imxdrm-objs := imx-drm-core.o ipuv3-crtc.o ipuv3-plane.o -+imxdrm-objs := imx-drm-core.o - - obj-$(CONFIG_DRM_IMX) += imxdrm.o - -@@ -8,5 +8,7 @@ obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) + - obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o - obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o - -+imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o -+obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o - obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o - obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdn-mhdp-imxdrv.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o cdn-mhdp-imx8qm.o cdn-mhdp-ls1028a.o ---- a/drivers/gpu/drm/imx/imx-drm-core.c -+++ b/drivers/gpu/drm/imx/imx-drm-core.c -@@ -343,23 +343,7 @@ static struct platform_driver imx_drm_pd - .of_match_table = imx_drm_dt_ids, - }, - }; -- --static struct platform_driver * const drivers[] = { -- &imx_drm_pdrv, -- &ipu_drm_driver, --}; -- --static int __init imx_drm_init(void) --{ -- return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_init(imx_drm_init); -- --static void __exit imx_drm_exit(void) --{ -- platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_exit(imx_drm_exit); -+module_platform_driver(imx_drm_pdrv); - - MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); - MODULE_DESCRIPTION("i.MX drm driver core"); ---- a/drivers/gpu/drm/imx/imx-drm.h -+++ b/drivers/gpu/drm/imx/imx-drm.h -@@ -28,8 +28,6 @@ int imx_drm_init_drm(struct platform_dev - int preferred_bpp); - int imx_drm_exit_drm(void); - --extern struct platform_driver ipu_drm_driver; -- - void imx_drm_mode_config_init(struct drm_device *drm); - - struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); ---- a/drivers/gpu/drm/imx/ipuv3-crtc.c -+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c -@@ -492,10 +492,16 @@ static int ipu_drm_remove(struct platfor - return 0; - } - --struct platform_driver ipu_drm_driver = { -+static struct platform_driver ipu_drm_driver = { - .driver = { - .name = "imx-ipuv3-crtc", - }, - .probe = ipu_drm_probe, - .remove = ipu_drm_remove, - }; -+module_platform_driver(ipu_drm_driver); -+ -+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:imx-ipuv3-crtc"); diff --git a/target/linux/layerscape/patches-5.4/805-display-0043-gpu-Move-ipu-v3-to-imx-folder.patch b/target/linux/layerscape/patches-5.4/805-display-0043-gpu-Move-ipu-v3-to-imx-folder.patch deleted file mode 100644 index 6deb30f885..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0043-gpu-Move-ipu-v3-to-imx-folder.patch +++ /dev/null @@ -1,19995 +0,0 @@ -From a7782c6e5e37b2b406221827b177c2bfcc8825cd Mon Sep 17 00:00:00 2001 -From: Liu Ying <victor.liu@nxp.com> -Date: Tue, 22 Jan 2019 17:08:01 +0800 -Subject: [PATCH] gpu: Move ipu-v3 to imx folder - -The new imx folder may contain ipu-v3 and dpu common drivers. - -Signed-off-by: Liu Ying <victor.liu@nxp.com> -[ Aisheng: fix source path ] -Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> ---- - drivers/gpu/Makefile | 2 +- - drivers/gpu/imx/Kconfig | 1 + - drivers/gpu/imx/Makefile | 1 + - drivers/gpu/imx/ipu-v3/Kconfig | 11 + - drivers/gpu/imx/ipu-v3/Makefile | 10 + - drivers/gpu/imx/ipu-v3/ipu-common.c | 1565 ++++++++++++++++++ - drivers/gpu/imx/ipu-v3/ipu-cpmem.c | 976 +++++++++++ - drivers/gpu/imx/ipu-v3/ipu-csi.c | 821 +++++++++ - drivers/gpu/imx/ipu-v3/ipu-dc.c | 420 +++++ - drivers/gpu/imx/ipu-v3/ipu-di.c | 745 +++++++++ - drivers/gpu/imx/ipu-v3/ipu-dmfc.c | 214 +++ - drivers/gpu/imx/ipu-v3/ipu-dp.c | 357 ++++ - drivers/gpu/imx/ipu-v3/ipu-ic.c | 761 +++++++++ - drivers/gpu/imx/ipu-v3/ipu-image-convert.c | 2475 ++++++++++++++++++++++++++++ - drivers/gpu/imx/ipu-v3/ipu-pre.c | 346 ++++ - drivers/gpu/imx/ipu-v3/ipu-prg.c | 483 ++++++ - drivers/gpu/imx/ipu-v3/ipu-prv.h | 274 +++ - drivers/gpu/imx/ipu-v3/ipu-smfc.c | 202 +++ - drivers/gpu/imx/ipu-v3/ipu-vdi.c | 234 +++ - drivers/gpu/ipu-v3/Kconfig | 11 - - drivers/gpu/ipu-v3/Makefile | 10 - - drivers/gpu/ipu-v3/ipu-common.c | 1565 ------------------ - drivers/gpu/ipu-v3/ipu-cpmem.c | 976 ----------- - drivers/gpu/ipu-v3/ipu-csi.c | 821 --------- - drivers/gpu/ipu-v3/ipu-dc.c | 420 ----- - drivers/gpu/ipu-v3/ipu-di.c | 745 --------- - drivers/gpu/ipu-v3/ipu-dmfc.c | 214 --- - drivers/gpu/ipu-v3/ipu-dp.c | 357 ---- - drivers/gpu/ipu-v3/ipu-ic.c | 761 --------- - drivers/gpu/ipu-v3/ipu-image-convert.c | 2475 ---------------------------- - drivers/gpu/ipu-v3/ipu-pre.c | 346 ---- - drivers/gpu/ipu-v3/ipu-prg.c | 483 ------ - drivers/gpu/ipu-v3/ipu-prv.h | 274 --- - drivers/gpu/ipu-v3/ipu-smfc.c | 202 --- - drivers/gpu/ipu-v3/ipu-vdi.c | 234 --- - drivers/video/Kconfig | 2 +- - 36 files changed, 9898 insertions(+), 9896 deletions(-) - create mode 100644 drivers/gpu/imx/Kconfig - create mode 100644 drivers/gpu/imx/Makefile - create mode 100644 drivers/gpu/imx/ipu-v3/Kconfig - create mode 100644 drivers/gpu/imx/ipu-v3/Makefile - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-common.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-cpmem.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-csi.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-dc.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-di.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-dmfc.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-dp.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-ic.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-image-convert.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-pre.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-prg.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-prv.h - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-smfc.c - create mode 100644 drivers/gpu/imx/ipu-v3/ipu-vdi.c - delete mode 100644 drivers/gpu/ipu-v3/Kconfig - delete mode 100644 drivers/gpu/ipu-v3/Makefile - delete mode 100644 drivers/gpu/ipu-v3/ipu-common.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-cpmem.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-csi.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-dc.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-di.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-dmfc.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-dp.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-ic.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-image-convert.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-pre.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-prg.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-prv.h - delete mode 100644 drivers/gpu/ipu-v3/ipu-smfc.c - delete mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c - ---- a/drivers/gpu/Makefile -+++ b/drivers/gpu/Makefile -@@ -3,5 +3,5 @@ - # taken to initialize them in the correct order. Link order is the only way - # to ensure this currently. - obj-$(CONFIG_TEGRA_HOST1X) += host1x/ -+obj-y += imx/ - obj-y += drm/ vga/ --obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- /dev/null -+++ b/drivers/gpu/imx/Kconfig -@@ -0,0 +1 @@ -+source "drivers/gpu/imx/ipu-v3/Kconfig" ---- /dev/null -+++ b/drivers/gpu/imx/Makefile -@@ -0,0 +1 @@ -+obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/Kconfig -@@ -0,0 +1,11 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+config IMX_IPUV3_CORE -+ tristate "IPUv3 core support" -+ depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST -+ depends on DRM || !DRM # if DRM=m, this can't be 'y' -+ select BITREVERSE -+ select GENERIC_ALLOCATOR if DRM -+ select GENERIC_IRQ_CHIP -+ help -+ Choose this if you have a i.MX5/6 system and want to use the Image -+ Processing Unit. This option only enables IPU base support. ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/Makefile -@@ -0,0 +1,10 @@ -+# SPDX-License-Identifier: GPL-2.0 -+obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -+ -+imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -+ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -+ ipu-image-convert.o ipu-smfc.o ipu-vdi.o -+ -+ifdef CONFIG_DRM -+ imx-ipu-v3-objs += ipu-pre.o ipu-prg.o -+endif ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-common.c -@@ -0,0 +1,1565 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/module.h> -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/reset.h> -+#include <linux/platform_device.h> -+#include <linux/err.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/clk.h> -+#include <linux/list.h> -+#include <linux/irq.h> -+#include <linux/irqchip/chained_irq.h> -+#include <linux/irqdomain.h> -+#include <linux/of_device.h> -+#include <linux/of_graph.h> -+ -+#include <drm/drm_fourcc.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->cm_reg + offset); -+} -+ -+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) -+{ -+ writel(value, ipu->cm_reg + offset); -+} -+ -+int ipu_get_num(struct ipu_soc *ipu) -+{ -+ return ipu->id; -+} -+EXPORT_SYMBOL_GPL(ipu_get_num); -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) -+{ -+ u32 val; -+ -+ val = ipu_cm_read(ipu, IPU_SRM_PRI2); -+ val &= ~DP_S_SRM_MODE_MASK; -+ val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -+ DP_S_SRM_MODE_NOW; -+ ipu_cm_write(ipu, val, IPU_SRM_PRI2); -+} -+EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -+ -+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_ARGB1555: -+ case DRM_FORMAT_ABGR1555: -+ case DRM_FORMAT_RGBA5551: -+ case DRM_FORMAT_BGRA5551: -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_ARGB4444: -+ case DRM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XBGR8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ return IPUV3_COLORSPACE_RGB; -+ case DRM_FORMAT_YUYV: -+ case DRM_FORMAT_UYVY: -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ case DRM_FORMAT_NV12: -+ case DRM_FORMAT_NV21: -+ case DRM_FORMAT_NV16: -+ case DRM_FORMAT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -+ -+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ return IPUV3_COLORSPACE_RGB; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -+ -+bool ipu_pixelformat_is_planar(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return true; -+ } -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -+ -+enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) -+{ -+ switch (mbus_code & 0xf000) { -+ case 0x1000: -+ return IPUV3_COLORSPACE_RGB; -+ case 0x2000: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -+ -+int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ /* -+ * for the planar YUV formats, the stride passed to -+ * cpmem must be the stride in bytes of the Y plane. -+ * And all the planar YUV formats have an 8-bit -+ * Y component. -+ */ -+ return (8 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_UYVY: -+ return (16 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ return (24 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_XRGB32: -+ return (32 * pixel_stride) >> 3; -+ default: -+ break; -+ } -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -+ -+int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ switch (degrees) { -+ case 0: -+ vf = hf = r90 = 0; -+ break; -+ case 90: -+ vf = hf = 0; -+ r90 = 1; -+ break; -+ case 180: -+ vf = hf = 1; -+ r90 = 0; -+ break; -+ case 270: -+ vf = hf = r90 = 1; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -+ -+int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ r90 = ((u32)mode >> 2) & 0x1; -+ hf = ((u32)mode >> 1) & 0x1; -+ vf = ((u32)mode >> 0) & 0x1; -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -+ case IPU_ROTATE_NONE: -+ *degrees = 0; -+ break; -+ case IPU_ROTATE_90_RIGHT: -+ *degrees = 90; -+ break; -+ case IPU_ROTATE_180: -+ *degrees = 180; -+ break; -+ case IPU_ROTATE_90_LEFT: -+ *degrees = 270; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -+ -+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) -+{ -+ struct ipuv3_channel *channel; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, num); -+ -+ if (num > 63) -+ return ERR_PTR(-ENODEV); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_for_each_entry(channel, &ipu->channels, list) { -+ if (channel->num == num) { -+ channel = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ } -+ -+ channel = kzalloc(sizeof(*channel), GFP_KERNEL); -+ if (!channel) { -+ channel = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ channel->num = num; -+ channel->ipu = ipu; -+ list_add(&channel->list, &ipu->channels); -+ -+out: -+ mutex_unlock(&ipu->channel_lock); -+ -+ return channel; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get); -+ -+void ipu_idmac_put(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_del(&channel->list); -+ kfree(channel); -+ -+ mutex_unlock(&ipu->channel_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_put); -+ -+#define idma_mask(ch) (1 << ((ch) & 0x1f)) -+ -+/* -+ * This is an undocumented feature, a write one to a channel bit in -+ * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -+ * internal current buffer pointer so that transfers start from buffer -+ * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -+ * only says these are read-only registers). This operation is required -+ * for channel linking to work correctly, for instance video capture -+ * pipelines that carry out image rotations will fail after the first -+ * streaming unless this function is called for each channel before -+ * re-enabling the channels. -+ */ -+static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); -+} -+ -+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -+ bool doublebuffer) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ if (doublebuffer) -+ reg |= idma_mask(channel->num); -+ else -+ reg &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -+ -+static const struct { -+ int chnum; -+ u32 reg; -+ int shift; -+} idmac_lock_en_info[] = { -+ { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -+ { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -+ { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -+ { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -+ { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -+ { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -+ { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -+ { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -+ { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -+ { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -+ { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -+ { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -+ { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -+ { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -+ { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -+ { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -+ { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, -+}; -+ -+int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 bursts, regval; -+ int i; -+ -+ switch (num_bursts) { -+ case 0: -+ case 1: -+ bursts = 0x00; /* locking disabled */ -+ break; -+ case 2: -+ bursts = 0x01; -+ break; -+ case 4: -+ bursts = 0x02; -+ break; -+ case 8: -+ bursts = 0x03; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* -+ * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -+ * i.MX53 channel arbitration locking doesn't seem to work properly. -+ * Allow enabling the lock feature on IPUv3H / i.MX6 only. -+ */ -+ if (bursts && ipu->ipu_type != IPUV3H) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -+ if (channel->num == idmac_lock_en_info[i].chnum) -+ break; -+ } -+ if (i >= ARRAY_SIZE(idmac_lock_en_info)) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -+ regval &= ~(0x03 << idmac_lock_en_info[i].shift); -+ regval |= (bursts << idmac_lock_en_info[i].shift); -+ ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val |= IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val |= IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val |= mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_enable); -+ -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val &= ~IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val &= ~IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_disable); -+ -+int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -+ -+bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg = 0; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ switch (buf_num) { -+ case 0: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -+ break; -+ case 1: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -+ break; -+ case 2: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -+ break; -+ } -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return ((reg & idma_mask(channel->num)) != 0); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -+ -+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Mark buffer as ready. */ -+ if (buf_num == 0) -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ else -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -+ -+void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -+ switch (buf_num) { -+ case 0: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ break; -+ case 1: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ break; -+ case 2: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -+ break; -+ default: -+ break; -+ } -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -+ -+int ipu_idmac_enable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val |= idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) -+{ -+ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -+ -+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long timeout; -+ -+ timeout = jiffies + msecs_to_jiffies(ms); -+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -+ idma_mask(channel->num)) { -+ if (time_after(jiffies, timeout)) -+ return -ETIMEDOUT; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -+ -+int ipu_idmac_disable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Disable DMA channel(s) */ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ /* Set channel buffers NOT to be ready */ -+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF0_RDY(channel->num)); -+ } -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF1_RDY(channel->num)); -+ } -+ -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ /* Reset the double buffer */ -+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -+ -+/* -+ * The imx6 rev. D TRM says that enabling the WM feature will increase -+ * a channel's priority. Refer to Table 36-8 Calculated priority value. -+ * The sub-module that is the sink or source for the channel must enable -+ * watermark signal for this to take effect (SMFC_WM for instance). -+ */ -+void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -+ if (enable) -+ val |= 1 << (channel->num % 32); -+ else -+ val &= ~(1 << (channel->num % 32)); -+ ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -+ -+static int ipu_memory_reset(struct ipu_soc *ipu) -+{ -+ unsigned long timeout; -+ -+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -+ -+ timeout = jiffies + msecs_to_jiffies(1000); -+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -+ if (time_after(jiffies, timeout)) -+ return -ETIME; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Set the source mux for the given CSI. Selects either parallel or -+ * MIPI CSI2 sources. -+ */ -+void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) -+{ -+ unsigned long flags; -+ u32 val, mask; -+ -+ mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -+ IPU_CONF_CSI0_DATA_SOURCE; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (mipi_csi2) -+ val |= mask; -+ else -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -+ -+/* -+ * Set the source mux for the IC. Selects either CSI[01] or the VDI. -+ */ -+void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) -+{ -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (vdi) -+ val |= IPU_CONF_IC_INPUT; -+ else -+ val &= ~IPU_CONF_IC_INPUT; -+ -+ if (csi_id == 1) -+ val |= IPU_CONF_CSI_SEL; -+ else -+ val &= ~IPU_CONF_CSI_SEL; -+ -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -+ -+ -+/* Frame Synchronization Unit Channel Linking */ -+ -+struct fsu_link_reg_info { -+ int chno; -+ u32 reg; -+ u32 mask; -+ u32 val; -+}; -+ -+struct fsu_link_info { -+ struct fsu_link_reg_info src; -+ struct fsu_link_reg_info sink; -+}; -+ -+static const struct fsu_link_info fsu_link_info[] = { -+ { -+ .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -+ FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -+ FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -+ FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -+ FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -+ }, { -+ .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -+ .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -+ FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -+ }, -+}; -+ -+static const struct fsu_link_info *find_fsu_link_info(int src, int sink) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -+ if (src == fsu_link_info[i].src.chno && -+ sink == fsu_link_info[i].sink.chno) -+ return &fsu_link_info[i]; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Links a source channel to a sink channel in the FSU. -+ */ -+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ src_reg |= link->src.val; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ sink_reg |= link->sink.val; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_link); -+ -+/* -+ * Unlinks source and sink channels in the FSU. -+ */ -+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -+ -+/* Link IDMAC channels in the FSU */ -+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_link(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_link); -+ -+/* Unlink IDMAC channels in the FSU */ -+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_unlink(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -+ -+struct ipu_devtype { -+ const char *name; -+ unsigned long cm_ofs; -+ unsigned long cpmem_ofs; -+ unsigned long srm_ofs; -+ unsigned long tpm_ofs; -+ unsigned long csi0_ofs; -+ unsigned long csi1_ofs; -+ unsigned long ic_ofs; -+ unsigned long disp0_ofs; -+ unsigned long disp1_ofs; -+ unsigned long dc_tmpl_ofs; -+ unsigned long vdi_ofs; -+ enum ipuv3_type type; -+}; -+ -+static struct ipu_devtype ipu_type_imx51 = { -+ .name = "IPUv3EX", -+ .cm_ofs = 0x1e000000, -+ .cpmem_ofs = 0x1f000000, -+ .srm_ofs = 0x1f040000, -+ .tpm_ofs = 0x1f060000, -+ .csi0_ofs = 0x1e030000, -+ .csi1_ofs = 0x1e038000, -+ .ic_ofs = 0x1e020000, -+ .disp0_ofs = 0x1e040000, -+ .disp1_ofs = 0x1e048000, -+ .dc_tmpl_ofs = 0x1f080000, -+ .vdi_ofs = 0x1e068000, -+ .type = IPUV3EX, -+}; -+ -+static struct ipu_devtype ipu_type_imx53 = { -+ .name = "IPUv3M", -+ .cm_ofs = 0x06000000, -+ .cpmem_ofs = 0x07000000, -+ .srm_ofs = 0x07040000, -+ .tpm_ofs = 0x07060000, -+ .csi0_ofs = 0x06030000, -+ .csi1_ofs = 0x06038000, -+ .ic_ofs = 0x06020000, -+ .disp0_ofs = 0x06040000, -+ .disp1_ofs = 0x06048000, -+ .dc_tmpl_ofs = 0x07080000, -+ .vdi_ofs = 0x06068000, -+ .type = IPUV3M, -+}; -+ -+static struct ipu_devtype ipu_type_imx6q = { -+ .name = "IPUv3H", -+ .cm_ofs = 0x00200000, -+ .cpmem_ofs = 0x00300000, -+ .srm_ofs = 0x00340000, -+ .tpm_ofs = 0x00360000, -+ .csi0_ofs = 0x00230000, -+ .csi1_ofs = 0x00238000, -+ .ic_ofs = 0x00220000, -+ .disp0_ofs = 0x00240000, -+ .disp1_ofs = 0x00248000, -+ .dc_tmpl_ofs = 0x00380000, -+ .vdi_ofs = 0x00268000, -+ .type = IPUV3H, -+}; -+ -+static const struct of_device_id imx_ipu_dt_ids[] = { -+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -+ { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -+ -+static int ipu_submodules_init(struct ipu_soc *ipu, -+ struct platform_device *pdev, unsigned long ipu_base, -+ struct clk *ipu_clk) -+{ -+ char *unit; -+ int ret; -+ struct device *dev = &pdev->dev; -+ const struct ipu_devtype *devtype = ipu->devtype; -+ -+ ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -+ if (ret) { -+ unit = "cpmem"; -+ goto err_cpmem; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -+ IPU_CONF_CSI0_EN, ipu_clk); -+ if (ret) { -+ unit = "csi0"; -+ goto err_csi_0; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -+ IPU_CONF_CSI1_EN, ipu_clk); -+ if (ret) { -+ unit = "csi1"; -+ goto err_csi_1; -+ } -+ -+ ret = ipu_ic_init(ipu, dev, -+ ipu_base + devtype->ic_ofs, -+ ipu_base + devtype->tpm_ofs); -+ if (ret) { -+ unit = "ic"; -+ goto err_ic; -+ } -+ -+ ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -+ IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -+ IPU_CONF_IC_INPUT); -+ if (ret) { -+ unit = "vdi"; -+ goto err_vdi; -+ } -+ -+ ret = ipu_image_convert_init(ipu, dev); -+ if (ret) { -+ unit = "image_convert"; -+ goto err_image_convert; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -+ IPU_CONF_DI0_EN, ipu_clk); -+ if (ret) { -+ unit = "di0"; -+ goto err_di_0; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -+ IPU_CONF_DI1_EN, ipu_clk); -+ if (ret) { -+ unit = "di1"; -+ goto err_di_1; -+ } -+ -+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -+ if (ret) { -+ unit = "dc_template"; -+ goto err_dc; -+ } -+ -+ ret = ipu_dmfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -+ if (ret) { -+ unit = "dmfc"; -+ goto err_dmfc; -+ } -+ -+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -+ if (ret) { -+ unit = "dp"; -+ goto err_dp; -+ } -+ -+ ret = ipu_smfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -+ if (ret) { -+ unit = "smfc"; -+ goto err_smfc; -+ } -+ -+ return 0; -+ -+err_smfc: -+ ipu_dp_exit(ipu); -+err_dp: -+ ipu_dmfc_exit(ipu); -+err_dmfc: -+ ipu_dc_exit(ipu); -+err_dc: -+ ipu_di_exit(ipu, 1); -+err_di_1: -+ ipu_di_exit(ipu, 0); -+err_di_0: -+ ipu_image_convert_exit(ipu); -+err_image_convert: -+ ipu_vdi_exit(ipu); -+err_vdi: -+ ipu_ic_exit(ipu); -+err_ic: -+ ipu_csi_exit(ipu, 1); -+err_csi_1: -+ ipu_csi_exit(ipu, 0); -+err_csi_0: -+ ipu_cpmem_exit(ipu); -+err_cpmem: -+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -+ return ret; -+} -+ -+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) -+{ -+ unsigned long status; -+ int i, bit, irq; -+ -+ for (i = 0; i < num_regs; i++) { -+ -+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -+ -+ for_each_set_bit(bit, &status, 32) { -+ irq = irq_linear_revmap(ipu->domain, -+ regs[i] * 32 + bit); -+ if (irq) -+ generic_handle_irq(irq); -+ } -+ } -+} -+ -+static void ipu_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+static void ipu_err_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 4, 5, 8, 9}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+int ipu_map_irq(struct ipu_soc *ipu, int irq) -+{ -+ int virq; -+ -+ virq = irq_linear_revmap(ipu->domain, irq); -+ if (!virq) -+ virq = irq_create_mapping(ipu->domain, irq); -+ -+ return virq; -+} -+EXPORT_SYMBOL_GPL(ipu_map_irq); -+ -+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -+ enum ipu_channel_irq irq_type) -+{ -+ return ipu_map_irq(ipu, irq_type + channel->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -+ -+static void ipu_submodules_exit(struct ipu_soc *ipu) -+{ -+ ipu_smfc_exit(ipu); -+ ipu_dp_exit(ipu); -+ ipu_dmfc_exit(ipu); -+ ipu_dc_exit(ipu); -+ ipu_di_exit(ipu, 1); -+ ipu_di_exit(ipu, 0); -+ ipu_image_convert_exit(ipu); -+ ipu_vdi_exit(ipu); -+ ipu_ic_exit(ipu); -+ ipu_csi_exit(ipu, 1); -+ ipu_csi_exit(ipu, 0); -+ ipu_cpmem_exit(ipu); -+} -+ -+static int platform_remove_devices_fn(struct device *dev, void *unused) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ -+ platform_device_unregister(pdev); -+ -+ return 0; -+} -+ -+static void platform_device_unregister_children(struct platform_device *pdev) -+{ -+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); -+} -+ -+struct ipu_platform_reg { -+ struct ipu_client_platformdata pdata; -+ const char *name; -+}; -+ -+/* These must be in the order of the corresponding device tree port nodes */ -+static struct ipu_platform_reg client_reg[] = { -+ { -+ .pdata = { -+ .csi = 0, -+ .dma[0] = IPUV3_CHANNEL_CSI0, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .csi = 1, -+ .dma[0] = IPUV3_CHANNEL_CSI1, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .di = 0, -+ .dc = 5, -+ .dp = IPU_DP_FLOW_SYNC_BG, -+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, { -+ .pdata = { -+ .di = 1, -+ .dc = 1, -+ .dp = -EINVAL, -+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu_client_id_mutex); -+static int ipu_client_id; -+ -+static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) -+{ -+ struct device *dev = ipu->dev; -+ unsigned i; -+ int id, ret; -+ -+ mutex_lock(&ipu_client_id_mutex); -+ id = ipu_client_id; -+ ipu_client_id += ARRAY_SIZE(client_reg); -+ mutex_unlock(&ipu_client_id_mutex); -+ -+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -+ struct ipu_platform_reg *reg = &client_reg[i]; -+ struct platform_device *pdev; -+ struct device_node *of_node; -+ -+ /* Associate subdevice with the corresponding port node */ -+ of_node = of_graph_get_port_by_id(dev->of_node, i); -+ if (!of_node) { -+ dev_info(dev, -+ "no port@%d node in %pOF, not using %s%d\n", -+ i, dev->of_node, -+ (i / 2) ? "DI" : "CSI", i % 2); -+ continue; -+ } -+ -+ pdev = platform_device_alloc(reg->name, id++); -+ if (!pdev) { -+ ret = -ENOMEM; -+ goto err_register; -+ } -+ -+ pdev->dev.parent = dev; -+ -+ reg->pdata.of_node = of_node; -+ ret = platform_device_add_data(pdev, ®->pdata, -+ sizeof(reg->pdata)); -+ if (!ret) -+ ret = platform_device_add(pdev); -+ if (ret) { -+ platform_device_put(pdev); -+ goto err_register; -+ } -+ } -+ -+ return 0; -+ -+err_register: -+ platform_device_unregister_children(to_platform_device(dev)); -+ -+ return ret; -+} -+ -+ -+static int ipu_irq_init(struct ipu_soc *ipu) -+{ -+ struct irq_chip_generic *gc; -+ struct irq_chip_type *ct; -+ unsigned long unused[IPU_NUM_IRQS / 32] = { -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x4077ffff, 0xffe7e1fd, -+ 0x23fffffe, 0x8880fff0, -+ 0xf98fe7d0, 0xfff81fff, -+ 0x400100d0, 0xffe000fd, -+ 0x00000000, -+ }; -+ int ret, i; -+ -+ ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -+ &irq_generic_chip_ops, ipu); -+ if (!ipu->domain) { -+ dev_err(ipu->dev, "failed to add irq domain\n"); -+ return -ENODEV; -+ } -+ -+ ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -+ handle_level_irq, 0, 0, 0); -+ if (ret < 0) { -+ dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -+ irq_domain_remove(ipu->domain); -+ return ret; -+ } -+ -+ /* Mask and clear all interrupts */ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -+ ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -+ } -+ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ gc = irq_get_domain_generic_chip(ipu->domain, i); -+ gc->reg_base = ipu->cm_reg; -+ gc->unused = unused[i / 32]; -+ ct = gc->chip_types; -+ ct->chip.irq_ack = irq_gc_ack_set_bit; -+ ct->chip.irq_mask = irq_gc_mask_clr_bit; -+ ct->chip.irq_unmask = irq_gc_mask_set_bit; -+ ct->regs.ack = IPU_INT_STAT(i / 32); -+ ct->regs.mask = IPU_INT_CTRL(i / 32); -+ } -+ -+ irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -+ irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -+ ipu); -+ -+ return 0; -+} -+ -+static void ipu_irq_exit(struct ipu_soc *ipu) -+{ -+ int i, irq; -+ -+ irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -+ irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -+ -+ /* TODO: remove irq_domain_generic_chips */ -+ -+ for (i = 0; i < IPU_NUM_IRQS; i++) { -+ irq = irq_linear_revmap(ipu->domain, i); -+ if (irq) -+ irq_dispose_mapping(irq); -+ } -+ -+ irq_domain_remove(ipu->domain); -+} -+ -+void ipu_dump(struct ipu_soc *ipu) -+{ -+ int i; -+ -+ dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -+ dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -+ for (i = 0; i < 15; i++) -+ dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -+ ipu_cm_read(ipu, IPU_INT_CTRL(i))); -+} -+EXPORT_SYMBOL_GPL(ipu_dump); -+ -+static int ipu_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct ipu_soc *ipu; -+ struct resource *res; -+ unsigned long ipu_base; -+ int ret, irq_sync, irq_err; -+ const struct ipu_devtype *devtype; -+ -+ devtype = of_device_get_match_data(&pdev->dev); -+ if (!devtype) -+ return -EINVAL; -+ -+ irq_sync = platform_get_irq(pdev, 0); -+ irq_err = platform_get_irq(pdev, 1); -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -+ irq_sync, irq_err); -+ -+ if (!res || irq_sync < 0 || irq_err < 0) -+ return -ENODEV; -+ -+ ipu_base = res->start; -+ -+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -+ if (!ipu) -+ return -ENODEV; -+ -+ ipu->id = of_alias_get_id(np, "ipu"); -+ if (ipu->id < 0) -+ ipu->id = 0; -+ -+ if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -+ IS_ENABLED(CONFIG_DRM)) { -+ ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -+ "fsl,prg", ipu->id); -+ if (!ipu->prg_priv) -+ return -EPROBE_DEFER; -+ } -+ -+ ipu->devtype = devtype; -+ ipu->ipu_type = devtype->type; -+ -+ spin_lock_init(&ipu->lock); -+ mutex_init(&ipu->channel_lock); -+ INIT_LIST_HEAD(&ipu->channels); -+ -+ dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs); -+ dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -+ dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -+ ipu_base + devtype->cpmem_ofs); -+ dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -+ ipu_base + devtype->csi0_ofs); -+ dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -+ ipu_base + devtype->csi1_ofs); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->ic_ofs); -+ dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -+ ipu_base + devtype->disp0_ofs); -+ dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -+ ipu_base + devtype->disp1_ofs); -+ dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -+ ipu_base + devtype->srm_ofs); -+ dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -+ ipu_base + devtype->tpm_ofs); -+ dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -+ dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -+ dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -+ ipu_base + devtype->vdi_ofs); -+ -+ ipu->cm_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs, PAGE_SIZE); -+ ipu->idmac_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -+ PAGE_SIZE); -+ -+ if (!ipu->cm_reg || !ipu->idmac_reg) -+ return -ENOMEM; -+ -+ ipu->clk = devm_clk_get(&pdev->dev, "bus"); -+ if (IS_ERR(ipu->clk)) { -+ ret = PTR_ERR(ipu->clk); -+ dev_err(&pdev->dev, "clk_get failed with %d", ret); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, ipu); -+ -+ ret = clk_prepare_enable(ipu->clk); -+ if (ret) { -+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -+ return ret; -+ } -+ -+ ipu->dev = &pdev->dev; -+ ipu->irq_sync = irq_sync; -+ ipu->irq_err = irq_err; -+ -+ ret = device_reset(&pdev->dev); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to reset: %d\n", ret); -+ goto out_failed_reset; -+ } -+ ret = ipu_memory_reset(ipu); -+ if (ret) -+ goto out_failed_reset; -+ -+ ret = ipu_irq_init(ipu); -+ if (ret) -+ goto out_failed_irq; -+ -+ /* Set MCU_T to divide MCU access window into 2 */ -+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -+ IPU_DISP_GEN); -+ -+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -+ if (ret) -+ goto failed_submodules_init; -+ -+ ret = ipu_add_client_devices(ipu, ipu_base); -+ if (ret) { -+ dev_err(&pdev->dev, "adding client devices failed with %d\n", -+ ret); -+ goto failed_add_clients; -+ } -+ -+ dev_info(&pdev->dev, "%s probed\n", devtype->name); -+ -+ return 0; -+ -+failed_add_clients: -+ ipu_submodules_exit(ipu); -+failed_submodules_init: -+ ipu_irq_exit(ipu); -+out_failed_irq: -+out_failed_reset: -+ clk_disable_unprepare(ipu->clk); -+ return ret; -+} -+ -+static int ipu_remove(struct platform_device *pdev) -+{ -+ struct ipu_soc *ipu = platform_get_drvdata(pdev); -+ -+ platform_device_unregister_children(pdev); -+ ipu_submodules_exit(ipu); -+ ipu_irq_exit(ipu); -+ -+ clk_disable_unprepare(ipu->clk); -+ -+ return 0; -+} -+ -+static struct platform_driver imx_ipu_driver = { -+ .driver = { -+ .name = "imx-ipuv3", -+ .of_match_table = imx_ipu_dt_ids, -+ }, -+ .probe = ipu_probe, -+ .remove = ipu_remove, -+}; -+ -+static struct platform_driver * const drivers[] = { -+#if IS_ENABLED(CONFIG_DRM) -+ &ipu_pre_drv, -+ &ipu_prg_drv, -+#endif -+ &imx_ipu_driver, -+}; -+ -+static int __init imx_ipu_init(void) -+{ -+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_init(imx_ipu_init); -+ -+static void __exit imx_ipu_exit(void) -+{ -+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_exit(imx_ipu_exit); -+ -+MODULE_ALIAS("platform:imx-ipuv3"); -+MODULE_DESCRIPTION("i.MX IPU v3 driver"); -+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-cpmem.c -@@ -0,0 +1,976 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#include <linux/types.h> -+#include <linux/bitrev.h> -+#include <linux/io.h> -+#include <linux/sizes.h> -+#include <drm/drm_fourcc.h> -+#include "ipu-prv.h" -+ -+struct ipu_cpmem_word { -+ u32 data[5]; -+ u32 res[3]; -+}; -+ -+struct ipu_ch_param { -+ struct ipu_cpmem_word word[2]; -+}; -+ -+struct ipu_cpmem { -+ struct ipu_ch_param __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -+ -+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) -+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) -+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) -+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) -+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) -+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -+ -+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) -+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) -+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) -+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) -+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) -+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) -+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) -+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) -+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) -+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) -+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) -+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) -+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) -+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) -+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) -+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) -+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) -+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) -+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) -+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) -+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) -+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) -+#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) -+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) -+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) -+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) -+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) -+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) -+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) -+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) -+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) -+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) -+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) -+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) -+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) -+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) -+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) -+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) -+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) -+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) -+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) -+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) -+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) -+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) -+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) -+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) -+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) -+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) -+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) -+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -+ -+static inline struct ipu_ch_param __iomem * -+ipu_get_cpmem(struct ipuv3_channel *ch) -+{ -+ struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -+ -+ return cpmem->base + ch->num; -+} -+ -+static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = readl(&base->word[word].data[i]); -+ val &= ~(mask << ofs); -+ val |= v << ofs; -+ writel(val, &base->word[word].data[i]); -+ -+ if ((bit + size - 1) / 32 > i) { -+ val = readl(&base->word[word].data[i + 1]); -+ val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -+ val |= v >> (ofs ? (32 - ofs) : 0); -+ writel(val, &base->word[word].data[i + 1]); -+ } -+} -+ -+static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val = 0; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = (readl(&base->word[word].data[i]) >> ofs) & mask; -+ -+ if ((bit + size - 1) / 32 > i) { -+ u32 tmp; -+ -+ tmp = readl(&base->word[word].data[i + 1]); -+ tmp &= mask >> (ofs ? (32 - ofs) : 0); -+ val |= tmp << (ofs ? (32 - ofs) : 0); -+ } -+ -+ return val; -+} -+ -+/* -+ * The V4L2 spec defines packed RGB formats in memory byte order, which from -+ * point of view of the IPU corresponds to little-endian words with the first -+ * component in the least significant bits. -+ * The DRM pixel formats and IPU internal representation are ordered the other -+ * way around, with the first named component ordered at the most significant -+ * bits. Further, V4L2 formats are not well defined: -+ * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -+ * We choose the interpretation which matches GStreamer behavior. -+ */ -+static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_RGB565: -+ /* -+ * Here we choose the 'corrected' interpretation of RGBP, a -+ * little-endian 16-bit word with the red component at the most -+ * significant bits: -+ * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -+ */ -+ return DRM_FORMAT_RGB565; -+ case V4L2_PIX_FMT_BGR24: -+ /* B G R <=> [24:0] R:G:B */ -+ return DRM_FORMAT_RGB888; -+ case V4L2_PIX_FMT_RGB24: -+ /* R G B <=> [24:0] B:G:R */ -+ return DRM_FORMAT_BGR888; -+ case V4L2_PIX_FMT_BGR32: -+ /* B G R A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_RGB32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ABGR32: -+ /* B G R A <=> [32:0] A:R:G:B */ -+ return DRM_FORMAT_ARGB8888; -+ case V4L2_PIX_FMT_XBGR32: -+ /* B G R X <=> [32:0] X:R:G:B */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_BGRA32: -+ /* A B G R <=> [32:0] R:G:B:A */ -+ return DRM_FORMAT_RGBA8888; -+ case V4L2_PIX_FMT_BGRX32: -+ /* X B G R <=> [32:0] R:G:B:X */ -+ return DRM_FORMAT_RGBX8888; -+ case V4L2_PIX_FMT_RGBA32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_ABGR8888; -+ case V4L2_PIX_FMT_RGBX32: -+ /* R G B X <=> [32:0] X:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ARGB32: -+ /* A R G B <=> [32:0] B:G:R:A */ -+ return DRM_FORMAT_BGRA8888; -+ case V4L2_PIX_FMT_XRGB32: -+ /* X R G B <=> [32:0] B:G:R:X */ -+ return DRM_FORMAT_BGRX8888; -+ case V4L2_PIX_FMT_UYVY: -+ return DRM_FORMAT_UYVY; -+ case V4L2_PIX_FMT_YUYV: -+ return DRM_FORMAT_YUYV; -+ case V4L2_PIX_FMT_YUV420: -+ return DRM_FORMAT_YUV420; -+ case V4L2_PIX_FMT_YUV422P: -+ return DRM_FORMAT_YUV422; -+ case V4L2_PIX_FMT_YVU420: -+ return DRM_FORMAT_YVU420; -+ case V4L2_PIX_FMT_NV12: -+ return DRM_FORMAT_NV12; -+ case V4L2_PIX_FMT_NV16: -+ return DRM_FORMAT_NV16; -+ } -+ -+ return -EINVAL; -+} -+ -+void ipu_cpmem_zero(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ void __iomem *base = p; -+ int i; -+ -+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -+ writel(0, base + i * sizeof(u32)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -+ -+void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -+ -+void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -+ -+void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -+ -+void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ u32 val; -+ -+ if (ipu->ipu_type == IPUV3EX) -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -+ val |= 1 << (ch->num % 32); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -+ -+void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) -+{ -+ WARN_ON_ONCE(buf & 0x7); -+ -+ if (bufnum) -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -+ else -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -+ -+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) -+{ -+ WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -+ -+void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -+ u32 pixelformat) -+{ -+ u32 ilo, sly, sluv; -+ -+ if (stride < 0) { -+ stride = -stride; -+ ilo = 0x100000 - (stride / 8); -+ } else { -+ ilo = stride / 8; -+ } -+ -+ sly = (stride * 2) - 1; -+ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ sluv = stride / 2 - 1; -+ break; -+ case V4L2_PIX_FMT_NV12: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_NV16: -+ sluv = stride * 2 - 1; -+ break; -+ default: -+ sluv = 0; -+ break; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -+ if (sluv) -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -+ -+void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) -+{ -+ id &= 0x3; -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -+ -+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) -+{ -+ return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -+ -+void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -+ -+void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -+ -+void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -+ enum ipu_rotate_mode rot) -+{ -+ u32 temp_rot = bitrev8(rot) >> 5; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -+ -+int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -+ const struct ipu_rgb *rgb) -+{ -+ int bpp = 0, npb = 0, ro, go, bo, to; -+ -+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -+ -+ if (rgb->transp.length) { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -+ rgb->transp.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -+ } else { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -+ rgb->bits_per_pixel); -+ } -+ -+ switch (rgb->bits_per_pixel) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -+ -+int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) -+{ -+ int bpp = 0, npb = 0; -+ -+ switch (width) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -+ -+void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) -+{ -+ switch (pixel_format) { -+ case V4L2_PIX_FMT_UYVY: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ case V4L2_PIX_FMT_YUYV: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -+ -+void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -+ unsigned int uv_stride, -+ unsigned int u_offset, unsigned int v_offset) -+{ -+ WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -+ -+static const struct ipu_rgb def_xrgb_32 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_xbgr_32 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgbx_32 = { -+ .red = { .offset = 24, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 8, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_bgrx_32 = { -+ .red = { .offset = 8, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 24, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgb_24 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_bgr_24 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_rgb_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16 = { -+ .red = { .offset = 10, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16_4444 = { -+ .red = { .offset = 8, .length = 4, }, -+ .green = { .offset = 4, .length = 4, }, -+ .blue = { .offset = 0, .length = 4, }, -+ .transp = { .offset = 12, .length = 4, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_abgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 10, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_rgba_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 1, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgra_16 = { -+ .red = { .offset = 1, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) -+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 4) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 2) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2)) + (x)) -+#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * y) + (x)) -+ -+#define NUM_ALPHA_CHANNELS 7 -+ -+/* See Table 37-12. Alpha channels mapping. */ -+static int ipu_channel_albm(int ch_num) -+{ -+ switch (ch_num) { -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -+ case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -+ case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -+ case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -+ case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ int albm; -+ u32 val; -+ -+ albm = ipu_channel_albm(ch->num); -+ if (albm < 0) -+ return; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -+ ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -+ val |= BIT(ch->num); -+ ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); -+} -+ -+int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV12: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV16: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_UYVY: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUYV: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_XBGR8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -+ break; -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_XRGB8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -+ break; -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_RGBX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -+ break; -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -+ break; -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_BGR888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -+ break; -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_RGB888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -+ break; -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_RGB565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -+ break; -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_BGR565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -+ break; -+ case DRM_FORMAT_ARGB1555: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16); -+ break; -+ case DRM_FORMAT_ABGR1555: -+ ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -+ break; -+ case DRM_FORMAT_RGBA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -+ break; -+ case DRM_FORMAT_BGRA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -+ break; -+ case DRM_FORMAT_ARGB4444: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_cpmem_set_separate_alpha(ch); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -+ -+int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) -+{ -+ struct v4l2_pix_format *pix = &image->pix; -+ int offset, u_offset, v_offset; -+ int ret = 0; -+ -+ pr_debug("%s: resolution: %dx%d stride: %d\n", -+ __func__, pix->width, pix->height, -+ pix->bytesperline); -+ -+ ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -+ ipu_cpmem_set_stride(ch, pix->bytesperline); -+ -+ ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -+ -+ switch (pix->pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YVU420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV12: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV16: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_RGB565: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ offset = image->rect.left * 4 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_BGR24: -+ offset = image->rect.left * 3 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR8: -+ case V4L2_PIX_FMT_SGBRG8: -+ case V4L2_PIX_FMT_SGRBG8: -+ case V4L2_PIX_FMT_SRGGB8: -+ case V4L2_PIX_FMT_GREY: -+ offset = image->rect.left + image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR16: -+ case V4L2_PIX_FMT_SGBRG16: -+ case V4L2_PIX_FMT_SGRBG16: -+ case V4L2_PIX_FMT_SRGGB16: -+ case V4L2_PIX_FMT_Y16: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ default: -+ /* This should not happen */ -+ WARN_ON(1); -+ offset = 0; -+ ret = -EINVAL; -+ } -+ -+ ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -+ ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -+ -+void ipu_cpmem_dump(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ struct ipu_soc *ipu = ch->ipu; -+ int chno = ch->num; -+ -+ dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[0].data[0]), -+ readl(&p->word[0].data[1]), -+ readl(&p->word[0].data[2]), -+ readl(&p->word[0].data[3]), -+ readl(&p->word[0].data[4])); -+ dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[1].data[0]), -+ readl(&p->word[1].data[1]), -+ readl(&p->word[1].data[2]), -+ readl(&p->word[1].data[3]), -+ readl(&p->word[1].data[4])); -+ dev_dbg(ipu->dev, "PFS 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -+ dev_dbg(ipu->dev, "BPP 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -+ dev_dbg(ipu->dev, "NPB 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -+ -+ dev_dbg(ipu->dev, "FW %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -+ dev_dbg(ipu->dev, "FH %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -+ dev_dbg(ipu->dev, "EBA0 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -+ dev_dbg(ipu->dev, "EBA1 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -+ dev_dbg(ipu->dev, "Stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -+ dev_dbg(ipu->dev, "scan_order %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -+ dev_dbg(ipu->dev, "uv_stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -+ dev_dbg(ipu->dev, "u_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -+ dev_dbg(ipu->dev, "v_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -+ -+ dev_dbg(ipu->dev, "Width0 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -+ dev_dbg(ipu->dev, "Width1 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -+ dev_dbg(ipu->dev, "Width2 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -+ dev_dbg(ipu->dev, "Width3 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -+ dev_dbg(ipu->dev, "Offset0 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -+ dev_dbg(ipu->dev, "Offset1 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -+ dev_dbg(ipu->dev, "Offset2 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -+ dev_dbg(ipu->dev, "Offset3 %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_cpmem *cpmem; -+ -+ cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -+ if (!cpmem) -+ return -ENOMEM; -+ -+ ipu->cpmem_priv = cpmem; -+ -+ spin_lock_init(&cpmem->lock); -+ cpmem->base = devm_ioremap(dev, base, SZ_128K); -+ if (!cpmem->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -+ base, cpmem->base); -+ cpmem->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_cpmem_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-csi.c -@@ -0,0 +1,821 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/videodev2.h> -+#include <uapi/linux/v4l2-mediabus.h> -+#include <linux/clk.h> -+#include <linux/clk-provider.h> -+#include <linux/clkdev.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_csi { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_ipu; /* IPU bus clock */ -+ spinlock_t lock; -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+/* CSI Register Offsets */ -+#define CSI_SENS_CONF 0x0000 -+#define CSI_SENS_FRM_SIZE 0x0004 -+#define CSI_ACT_FRM_SIZE 0x0008 -+#define CSI_OUT_FRM_CTRL 0x000c -+#define CSI_TST_CTRL 0x0010 -+#define CSI_CCIR_CODE_1 0x0014 -+#define CSI_CCIR_CODE_2 0x0018 -+#define CSI_CCIR_CODE_3 0x001c -+#define CSI_MIPI_DI 0x0020 -+#define CSI_SKIP 0x0024 -+#define CSI_CPD_CTRL 0x0028 -+#define CSI_CPD_RC(n) (0x002c + ((n)*4)) -+#define CSI_CPD_RS(n) (0x004c + ((n)*4)) -+#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) -+#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) -+#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) -+#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) -+#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) -+#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) -+#define CSI_CPD_OFFSET1 0x00ec -+#define CSI_CPD_OFFSET2 0x00f0 -+ -+/* CSI Register Fields */ -+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -+#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 -+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L -+#define CSI_SENS_CONF_DATA_FMT_BAYER 3L -+#define CSI_SENS_CONF_DATA_FMT_RGB565 4L -+#define CSI_SENS_CONF_DATA_FMT_RGB555 5L -+#define CSI_SENS_CONF_DATA_FMT_RGB444 6L -+#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -+ -+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -+#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -+#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 -+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -+#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 -+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 -+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -+ -+#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 -+#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 -+#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 -+#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 -+#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 -+#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 -+#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -+ -+#define CSI_DATA_DEST_IC 2 -+#define CSI_DATA_DEST_IDMAC 4 -+ -+#define CSI_CCIR_ERR_DET_EN 0x01000000 -+#define CSI_HORI_DOWNSIZE_EN 0x80000000 -+#define CSI_VERT_DOWNSIZE_EN 0x40000000 -+#define CSI_TEST_GEN_MODE_EN 0x01000000 -+ -+#define CSI_HSC_MASK 0x1fff0000 -+#define CSI_HSC_SHIFT 16 -+#define CSI_VSC_MASK 0x00000fff -+#define CSI_VSC_SHIFT 0 -+ -+#define CSI_TEST_GEN_R_MASK 0x000000ff -+#define CSI_TEST_GEN_R_SHIFT 0 -+#define CSI_TEST_GEN_G_MASK 0x0000ff00 -+#define CSI_TEST_GEN_G_SHIFT 8 -+#define CSI_TEST_GEN_B_MASK 0x00ff0000 -+#define CSI_TEST_GEN_B_SHIFT 16 -+ -+#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 -+#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 -+#define CSI_SKIP_SMFC_MASK 0x000000f8 -+#define CSI_SKIP_SMFC_SHIFT 3 -+#define CSI_ID_2_SKIP_MASK 0x00000300 -+#define CSI_ID_2_SKIP_SHIFT 8 -+ -+#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 -+#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -+ -+/* MIPI CSI-2 data types */ -+#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ -+#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ -+#define MIPI_DT_YUV422 0x1e /* UYVY... */ -+#define MIPI_DT_RGB444 0x20 -+#define MIPI_DT_RGB555 0x21 -+#define MIPI_DT_RGB565 0x22 -+#define MIPI_DT_RGB666 0x23 -+#define MIPI_DT_RGB888 0x24 -+#define MIPI_DT_RAW6 0x28 -+#define MIPI_DT_RAW7 0x29 -+#define MIPI_DT_RAW8 0x2a -+#define MIPI_DT_RAW10 0x2b -+#define MIPI_DT_RAW12 0x2c -+#define MIPI_DT_RAW14 0x2d -+ -+/* -+ * Bitfield of CSI bus signal polarities and modes. -+ */ -+struct ipu_csi_bus_config { -+ unsigned data_width:4; -+ unsigned clk_mode:3; -+ unsigned ext_vsync:1; -+ unsigned vsync_pol:1; -+ unsigned hsync_pol:1; -+ unsigned pixclk_pol:1; -+ unsigned data_pol:1; -+ unsigned sens_clksrc:1; -+ unsigned pack_tight:1; -+ unsigned force_eof:1; -+ unsigned data_en_pol:1; -+ -+ unsigned data_fmt; -+ unsigned mipi_dt; -+}; -+ -+/* -+ * Enumeration of CSI data bus widths. -+ */ -+enum ipu_csi_data_width { -+ IPU_CSI_DATA_WIDTH_4 = 0, -+ IPU_CSI_DATA_WIDTH_8 = 1, -+ IPU_CSI_DATA_WIDTH_10 = 3, -+ IPU_CSI_DATA_WIDTH_12 = 5, -+ IPU_CSI_DATA_WIDTH_16 = 9, -+}; -+ -+/* -+ * Enumeration of CSI clock modes. -+ */ -+enum ipu_csi_clk_mode { -+ IPU_CSI_CLK_MODE_GATED_CLK, -+ IPU_CSI_CLK_MODE_NONGATED_CLK, -+ IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -+ IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, -+}; -+ -+static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) -+{ -+ return readl(csi->base + offset); -+} -+ -+static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -+ unsigned offset) -+{ -+ writel(value, csi->base + offset); -+} -+ -+/* -+ * Set mclk division ratio for generating test mode mclk. Only used -+ * for test generator. -+ */ -+static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -+ u32 ipu_clk) -+{ -+ u32 temp; -+ int div_ratio; -+ -+ div_ratio = (ipu_clk / pixel_clk) - 1; -+ -+ if (div_ratio > 0xFF || div_ratio < 0) { -+ dev_err(csi->ipu->dev, -+ "value of pixel_clk extends normal range\n"); -+ return -EINVAL; -+ } -+ -+ temp = ipu_csi_read(csi, CSI_SENS_CONF); -+ temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -+ ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -+ CSI_SENS_CONF); -+ -+ return 0; -+} -+ -+/* -+ * Find the CSI data format and data width for the given V4L2 media -+ * bus pixel format code. -+ */ -+static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -+ enum v4l2_mbus_type mbus_type) -+{ -+ switch (mbus_code) { -+ case MEDIA_BUS_FMT_BGR565_2X8_BE: -+ case MEDIA_BUS_FMT_BGR565_2X8_LE: -+ case MEDIA_BUS_FMT_RGB565_2X8_BE: -+ case MEDIA_BUS_FMT_RGB565_2X8_LE: -+ if (mbus_type == V4L2_MBUS_CSI2_DPHY) -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -+ else -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RGB565; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -+ cfg->mipi_dt = MIPI_DT_RGB444; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -+ cfg->mipi_dt = MIPI_DT_RGB555; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -+ cfg->mipi_dt = MIPI_DT_RGB888; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_YUYV8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_16; -+ break; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ case MEDIA_BUS_FMT_Y8_1X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ case MEDIA_BUS_FMT_Y10_1X10: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_10; -+ break; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ case MEDIA_BUS_FMT_Y12_1X12: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW12; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_12; -+ break; -+ case MEDIA_BUS_FMT_JPEG_1X8: -+ /* TODO */ -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* translate alternate field mode based on given standard */ -+static inline enum v4l2_field -+ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) -+{ -+ return (field != V4L2_FIELD_ALTERNATE) ? field : -+ ((std & V4L2_STD_525_60) ? -+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); -+} -+ -+/* -+ * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -+ */ -+static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ int ret; -+ -+ memset(csicfg, 0, sizeof(*csicfg)); -+ -+ ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -+ if (ret < 0) -+ return ret; -+ -+ switch (mbus_cfg->type) { -+ case V4L2_MBUS_PARALLEL: -+ csicfg->ext_vsync = 1; -+ csicfg->vsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->hsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->pixclk_pol = (mbus_cfg->flags & -+ V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -+ break; -+ case V4L2_MBUS_BT656: -+ csicfg->ext_vsync = 0; -+ if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -+ mbus_fmt->field == V4L2_FIELD_ALTERNATE) -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -+ else -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -+ break; -+ case V4L2_MBUS_CSI2_DPHY: -+ /* -+ * MIPI CSI-2 requires non gated clock mode, all other -+ * parameters are not applicable for MIPI CSI-2 bus. -+ */ -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -+ break; -+ default: -+ /* will never get here, keep compiler quiet */ -+ break; -+ } -+ -+ return 0; -+} -+ -+static int -+ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt, -+ v4l2_std_id std) -+{ -+ enum v4l2_field infield, outfield; -+ bool swap_fields; -+ -+ /* get translated field type of input and output */ -+ infield = ipu_csi_translate_field(infmt->field, std); -+ outfield = ipu_csi_translate_field(outfmt->field, std); -+ -+ /* -+ * Write the H-V-F codes the CSI will match against the -+ * incoming data for start/end of active and blanking -+ * field intervals. If input and output field types are -+ * sequential but not the same (one is SEQ_BT and the other -+ * is SEQ_TB), swap the F-bit so that the CSI will capture -+ * field 1 lines before field 0 lines. -+ */ -+ swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -+ V4L2_FIELD_IS_SEQUENTIAL(outfield) && -+ infield != outfield); -+ -+ if (!swap_fields) { -+ /* -+ * Field0BlankEnd = 110, Field0BlankStart = 010 -+ * Field0ActiveEnd = 100, Field0ActiveStart = 000 -+ * Field1BlankEnd = 111, Field1BlankStart = 011 -+ * Field1ActiveEnd = 101, Field1ActiveStart = 001 -+ */ -+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -+ } else { -+ dev_dbg(csi->ipu->dev, "capture field swap\n"); -+ -+ /* same as above but with F-bit inverted */ -+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -+ } -+ -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ -+ return 0; -+} -+ -+ -+int ipu_csi_init_interface(struct ipu_csi *csi, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 width, height, data = 0; -+ v4l2_std_id std; -+ int ret; -+ -+ ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -+ if (ret < 0) -+ return ret; -+ -+ /* set default sensor frame width and height */ -+ width = infmt->width; -+ height = infmt->height; -+ if (infmt->field == V4L2_FIELD_ALTERNATE) -+ height *= 2; -+ -+ /* Set the CSI_SENS_CONF register remaining fields */ -+ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -+ cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -+ cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -+ cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -+ cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -+ cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -+ cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -+ cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -+ cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -+ cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -+ cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, data, CSI_SENS_CONF); -+ -+ /* Set CCIR registers */ -+ -+ switch (cfg.clk_mode) { -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ if (width == 720 && height == 480) { -+ std = V4L2_STD_NTSC; -+ height = 525; -+ } else if (width == 720 && height == 576) { -+ std = V4L2_STD_PAL; -+ height = 625; -+ } else { -+ dev_err(csi->ipu->dev, -+ "Unsupported interlaced video mode\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -+ if (ret) -+ goto out_unlock; -+ break; -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -+ break; -+ } -+ -+ /* Setup sensor frame size */ -+ ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -+ CSI_SENS_FRM_SIZE); -+ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ -+out_unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -+ -+bool ipu_csi_is_interlaced(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ u32 sensor_protocol; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ sensor_protocol = -+ (ipu_csi_read(csi, CSI_SENS_CONF) & -+ CSI_SENS_CONF_SENS_PRTCL_MASK) >> -+ CSI_SENS_CONF_SENS_PRTCL_SHIFT; -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ switch (sensor_protocol) { -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ return false; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ return true; -+ default: -+ dev_err(csi->ipu->dev, -+ "CSI %d sensor protocol unsupported\n", csi->id); -+ return false; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -+ -+void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -+ w->width = (reg & 0xFFFF) + 1; -+ w->height = (reg >> 16 & 0xFFFF) + 1; -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -+ w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get_window); -+ -+void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -+ CSI_ACT_FRM_SIZE); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -+ reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_window); -+ -+void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -+ reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -+ (vert ? CSI_VERT_DOWNSIZE_EN : 0); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -+ -+void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -+ u32 r_value, u32 g_value, u32 b_value, -+ u32 pix_clk) -+{ -+ unsigned long flags; -+ u32 ipu_clk = clk_get_rate(csi->clk_ipu); -+ u32 temp; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_TST_CTRL); -+ -+ if (!active) { -+ temp &= ~CSI_TEST_GEN_MODE_EN; -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } else { -+ /* Set sensb_mclk div_ratio */ -+ ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -+ -+ temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -+ CSI_TEST_GEN_B_MASK); -+ temp |= CSI_TEST_GEN_MODE_EN; -+ temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -+ (g_value << CSI_TEST_GEN_G_SHIFT) | -+ (b_value << CSI_TEST_GEN_B_SHIFT); -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -+ -+int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -+ struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 temp; -+ int ret; -+ -+ if (vc > 3) -+ return -EINVAL; -+ -+ ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -+ if (ret < 0) -+ return ret; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_MIPI_DI); -+ temp &= ~(0xff << (vc * 8)); -+ temp |= (cfg.mipi_dt << (vc * 8)); -+ ipu_csi_write(csi, temp, CSI_MIPI_DI); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -+ -+int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -+ u32 max_ratio, u32 id) -+{ -+ unsigned long flags; -+ u32 temp; -+ -+ if (max_ratio > 5 || id > 3) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_SKIP); -+ temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -+ CSI_SKIP_SMFC_MASK); -+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -+ (id << CSI_ID_2_SKIP_SHIFT) | -+ (skip << CSI_SKIP_SMFC_SHIFT); -+ ipu_csi_write(csi, temp, CSI_SKIP); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -+ -+int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) -+{ -+ unsigned long flags; -+ u32 csi_sens_conf, dest; -+ -+ if (csi_dest == IPU_CSI_DEST_IDMAC) -+ dest = CSI_DATA_DEST_IDMAC; -+ else -+ dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -+ csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -+ csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -+ ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -+ -+int ipu_csi_enable(struct ipu_csi *csi) -+{ -+ ipu_module_enable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_enable); -+ -+int ipu_csi_disable(struct ipu_csi *csi) -+{ -+ ipu_module_disable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_disable); -+ -+struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) -+{ -+ unsigned long flags; -+ struct ipu_csi *csi, *ret; -+ -+ if (id > 1) -+ return ERR_PTR(-EINVAL); -+ -+ csi = ipu->csi_priv[id]; -+ ret = csi; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ if (csi->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ csi->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get); -+ -+void ipu_csi_put(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ csi->inuse = false; -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_put); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_csi *csi; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -+ if (!csi) -+ return -ENOMEM; -+ -+ ipu->csi_priv[id] = csi; -+ -+ spin_lock_init(&csi->lock); -+ csi->module = module; -+ csi->id = id; -+ csi->clk_ipu = clk_ipu; -+ csi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!csi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -+ id, base, csi->base); -+ csi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_csi_exit(struct ipu_soc *ipu, int id) -+{ -+} -+ -+void ipu_csi_dump(struct ipu_csi *csi) -+{ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_TST_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_1)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_2)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_3)); -+ dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -+ ipu_csi_read(csi, CSI_MIPI_DI)); -+ dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -+ ipu_csi_read(csi, CSI_SKIP)); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-dc.c -@@ -0,0 +1,420 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) -+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -+ -+#define DC_EVT_NF 0 -+#define DC_EVT_NL 1 -+#define DC_EVT_EOF 2 -+#define DC_EVT_NFIELD 3 -+#define DC_EVT_EOL 4 -+#define DC_EVT_EOFIELD 5 -+#define DC_EVT_NEW_ADDR 6 -+#define DC_EVT_NEW_CHAN 7 -+#define DC_EVT_NEW_DATA 8 -+ -+#define DC_EVT_NEW_ADDR_W_0 0 -+#define DC_EVT_NEW_ADDR_W_1 1 -+#define DC_EVT_NEW_CHAN_W_0 2 -+#define DC_EVT_NEW_CHAN_W_1 3 -+#define DC_EVT_NEW_DATA_W_0 4 -+#define DC_EVT_NEW_DATA_W_1 5 -+#define DC_EVT_NEW_ADDR_R_0 6 -+#define DC_EVT_NEW_ADDR_R_1 7 -+#define DC_EVT_NEW_CHAN_R_0 8 -+#define DC_EVT_NEW_CHAN_R_1 9 -+#define DC_EVT_NEW_DATA_R_0 10 -+#define DC_EVT_NEW_DATA_R_1 11 -+ -+#define DC_WR_CH_CONF 0x0 -+#define DC_WR_CH_ADDR 0x4 -+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -+ -+#define DC_GEN 0xd4 -+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) -+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) -+#define DC_STAT 0x1c8 -+ -+#define WROD(lf) (0x18 | ((lf) << 1)) -+#define WRG 0x01 -+#define WCLK 0xc9 -+ -+#define SYNC_WAVE 0 -+#define NULL_WAVE (-1) -+ -+#define DC_GEN_SYNC_1_6_SYNC (2 << 1) -+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -+ -+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) -+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) -+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) -+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) -+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) -+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) -+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) -+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) -+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -+ -+#define IPU_DC_NUM_CHANNELS 10 -+ -+struct ipu_dc_priv; -+ -+enum ipu_dc_map { -+ IPU_DC_MAP_RGB24, -+ IPU_DC_MAP_RGB565, -+ IPU_DC_MAP_GBR24, /* TVEv2 */ -+ IPU_DC_MAP_BGR666, -+ IPU_DC_MAP_LVDS666, -+ IPU_DC_MAP_BGR24, -+}; -+ -+struct ipu_dc { -+ /* The display interface number assigned to this dc channel */ -+ unsigned int di; -+ void __iomem *base; -+ struct ipu_dc_priv *priv; -+ int chno; -+ bool in_use; -+}; -+ -+struct ipu_dc_priv { -+ void __iomem *dc_reg; -+ void __iomem *dc_tmpl_reg; -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -+ struct mutex mutex; -+ struct completion comp; -+ int use_count; -+}; -+ -+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_RL_CH(event)); -+ reg &= ~(0xffff << (16 * (event & 0x1))); -+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -+ writel(reg, dc->base + DC_RL_CH(event)); -+} -+ -+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -+ int map, int wave, int glue, int sync, int stop) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ u32 reg1, reg2; -+ -+ if (opcode == WCLK) { -+ reg1 = (operand << 20) & 0xfff00000; -+ reg2 = operand >> 12 | opcode << 1 | stop << 9; -+ } else if (opcode == WRG) { -+ reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -+ reg2 = operand >> 17 | opcode << 7 | stop << 9; -+ } else { -+ reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -+ reg2 = operand >> 12 | opcode << 4 | stop << 9; -+ } -+ writel(reg1, priv->dc_tmpl_reg + word * 8); -+ writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); -+} -+ -+static int ipu_bus_format_to_map(u32 fmt) -+{ -+ switch (fmt) { -+ default: -+ WARN_ON(1); -+ /* fall-through */ -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return IPU_DC_MAP_RGB24; -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return IPU_DC_MAP_RGB565; -+ case MEDIA_BUS_FMT_GBR888_1X24: -+ return IPU_DC_MAP_GBR24; -+ case MEDIA_BUS_FMT_RGB666_1X18: -+ return IPU_DC_MAP_BGR666; -+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -+ return IPU_DC_MAP_LVDS666; -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ return IPU_DC_MAP_BGR24; -+ } -+} -+ -+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -+ u32 bus_format, u32 width) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ int addr, sync; -+ u32 reg = 0; -+ int map; -+ -+ dc->di = ipu_di_get_num(di); -+ -+ map = ipu_bus_format_to_map(bus_format); -+ -+ /* -+ * In interlaced mode we need more counters to create the asymmetric -+ * per-field VSYNC signals. The pixel active signal synchronising DC -+ * to DI moves to signal generator #6 (see ipu-di.c). In progressive -+ * mode counter #5 is used. -+ */ -+ sync = interlaced ? 6 : 5; -+ -+ /* Reserve 5 microcode template words for each DI */ -+ if (dc->di) -+ addr = 5; -+ else -+ addr = 0; -+ -+ if (interlaced) { -+ dc_link_event(dc, DC_EVT_NL, addr, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } else { -+ dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -+ dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -+ dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -+ dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } -+ -+ dc_link_event(dc, DC_EVT_NF, 0, 0); -+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_EOF, 0, 0); -+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ if (interlaced) -+ reg |= DC_WR_CH_CONF_FIELD_MODE; -+ else -+ reg &= ~DC_WR_CH_CONF_FIELD_MODE; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+ -+ writel(0x0, dc->base + DC_WR_CH_ADDR); -+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -+ -+void ipu_dc_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable); -+ -+void ipu_dc_enable_channel(struct ipu_dc *dc) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -+ -+void ipu_dc_disable_channel(struct ipu_dc *dc) -+{ -+ u32 val; -+ -+ val = readl(dc->base + DC_WR_CH_CONF); -+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -+ writel(val, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -+ -+void ipu_dc_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable); -+ -+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -+ int byte_num, int offset, int mask) -+{ -+ int ptr = map * 3 + byte_num; -+ u32 reg; -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ reg &= ~(0xffff << (16 * (ptr & 0x1))); -+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) -+{ -+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ -+ writel(reg & ~(0xffff << (16 * (map & 0x1))), -+ priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ struct ipu_dc *dc; -+ -+ if (channel >= IPU_DC_NUM_CHANNELS) -+ return ERR_PTR(-ENODEV); -+ -+ dc = &priv->channels[channel]; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (dc->in_use) { -+ mutex_unlock(&priv->mutex); -+ return ERR_PTR(-EBUSY); -+ } -+ -+ dc->in_use = true; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return dc; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_get); -+ -+void ipu_dc_put(struct ipu_dc *dc) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ -+ mutex_lock(&priv->mutex); -+ dc->in_use = false; -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_put); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long template_base) -+{ -+ struct ipu_dc_priv *priv; -+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -+ 0x78, 0, 0x94, 0xb4}; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -+ if (!priv->dc_reg || !priv->dc_tmpl_reg) -+ return -ENOMEM; -+ -+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -+ priv->channels[i].chno = i; -+ priv->channels[i].priv = priv; -+ priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -+ } -+ -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -+ DC_WR_CH_CONF_PROG_DI_ID, -+ priv->channels[1].base + DC_WR_CH_CONF); -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -+ priv->channels[5].base + DC_WR_CH_CONF); -+ -+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -+ priv->dc_reg + DC_GEN); -+ -+ ipu->dc_priv = priv; -+ -+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -+ base, template_base); -+ -+ /* rgb24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -+ -+ /* rgb565 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -+ -+ /* gbr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -+ -+ /* bgr666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -+ -+ /* lvds666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -+ -+ /* bgr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -+ -+ return 0; -+} -+ -+void ipu_dc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-di.c -@@ -0,0 +1,745 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+struct ipu_di { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_di; /* display input clock */ -+ struct clk *clk_ipu; /* IPU bus clock */ -+ struct clk *clk_di_pixel; /* resulting pixel clock */ -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+static DEFINE_MUTEX(di_mutex); -+ -+struct di_sync_config { -+ int run_count; -+ int run_src; -+ int offset_count; -+ int offset_src; -+ int repeat_count; -+ int cnt_clr_src; -+ int cnt_polarity_gen_en; -+ int cnt_polarity_clr_src; -+ int cnt_polarity_trigger_src; -+ int cnt_up; -+ int cnt_down; -+}; -+ -+enum di_pins { -+ DI_PIN11 = 0, -+ DI_PIN12 = 1, -+ DI_PIN13 = 2, -+ DI_PIN14 = 3, -+ DI_PIN15 = 4, -+ DI_PIN16 = 5, -+ DI_PIN17 = 6, -+ DI_PIN_CS = 7, -+ -+ DI_PIN_SER_CLK = 0, -+ DI_PIN_SER_RS = 1, -+}; -+ -+enum di_sync_wave { -+ DI_SYNC_NONE = 0, -+ DI_SYNC_CLK = 1, -+ DI_SYNC_INT_HSYNC = 2, -+ DI_SYNC_HSYNC = 3, -+ DI_SYNC_VSYNC = 4, -+ DI_SYNC_DE = 6, -+ -+ DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -+ DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -+ DI_SYNC_CNT5 = 6, /* counter >= 6 only */ -+}; -+ -+#define SYNC_WAVE 0 -+ -+#define DI_GENERAL 0x0000 -+#define DI_BS_CLKGEN0 0x0004 -+#define DI_BS_CLKGEN1 0x0008 -+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) -+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) -+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) -+#define DI_SYNC_AS_GEN 0x0054 -+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) -+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) -+#define DI_SER_CONF 0x015c -+#define DI_SSC 0x0160 -+#define DI_POL 0x0164 -+#define DI_AW0 0x0168 -+#define DI_AW1 0x016c -+#define DI_SCR_CONF 0x0170 -+#define DI_STAT 0x0174 -+ -+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) -+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) -+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) -+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -+ -+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) -+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) -+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) -+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) -+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) -+#define DI_SW_GEN1_CNT_UP(x) (x) -+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -+ -+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 -+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -+ -+#define DI_GEN_POLARITY_1 (1 << 0) -+#define DI_GEN_POLARITY_2 (1 << 1) -+#define DI_GEN_POLARITY_3 (1 << 2) -+#define DI_GEN_POLARITY_4 (1 << 3) -+#define DI_GEN_POLARITY_5 (1 << 4) -+#define DI_GEN_POLARITY_6 (1 << 5) -+#define DI_GEN_POLARITY_7 (1 << 6) -+#define DI_GEN_POLARITY_8 (1 << 7) -+#define DI_GEN_POLARITY_DISP_CLK (1 << 17) -+#define DI_GEN_DI_CLK_EXT (1 << 20) -+#define DI_GEN_DI_VSYNC_EXT (1 << 21) -+ -+#define DI_POL_DRDY_DATA_POLARITY (1 << 7) -+#define DI_POL_DRDY_POLARITY_15 (1 << 4) -+ -+#define DI_VSYNC_SEL_OFFSET 13 -+ -+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) -+{ -+ return readl(di->base + offset); -+} -+ -+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) -+{ -+ writel(value, di->base + offset); -+} -+ -+static void ipu_di_data_wave_config(struct ipu_di *di, -+ int wave_gen, -+ int access_size, int component_size) -+{ -+ u32 reg; -+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+} -+ -+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -+ int set, int up, int down) -+{ -+ u32 reg; -+ -+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -+ reg &= ~(0x3 << (di_pin * 2)); -+ reg |= set << (di_pin * 2); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+ -+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); -+} -+ -+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -+ int start, int count) -+{ -+ u32 reg; -+ int i; -+ -+ for (i = 0; i < count; i++) { -+ struct di_sync_config *c = &config[i]; -+ int wave_gen = start + i + 1; -+ -+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -+ (c->repeat_count >= 0x1000) || -+ (c->cnt_up >= 0x400) || -+ (c->cnt_down >= 0x400)) { -+ dev_err(di->ipu->dev, "DI%d counters out of range.\n", -+ di->id); -+ return; -+ } -+ -+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -+ DI_SW_GEN0_RUN_SRC(c->run_src) | -+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -+ DI_SW_GEN0_OFFSET_SRC(c->offset_src); -+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -+ -+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -+ c->cnt_polarity_trigger_src) | -+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -+ DI_SW_GEN1_CNT_UP(c->cnt_up); -+ -+ /* Enable auto reload */ -+ if (c->repeat_count == 0) -+ reg |= DI_SW_GEN1_AUTO_RELOAD; -+ -+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -+ -+ reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -+ ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -+ } -+} -+ -+static void ipu_di_sync_config_interlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: internal VSYNC for each frame */ -+ .run_count = v_total * 2 - 1, -+ .run_src = 3, /* == counter 7 */ -+ }, { -+ /* PIN2: HSYNC waveform */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ }, { -+ /* PIN3: VSYNC waveform */ -+ .run_count = v_total - 1, -+ .run_src = 4, /* == counter 7 */ -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = 4, /* == counter 7 */ -+ .cnt_down = sig->mode.vsync_len * 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 4: Field */ -+ .run_count = v_total / 2, -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = h_total / 2, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 5: Active lines */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = (sig->mode.vsync_len + -+ sig->mode.vback_porch) / 2, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive / 2, -+ .cnt_clr_src = DI_SYNC_CNT4, -+ }, { -+ /* 6: Active pixel, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = DI_SYNC_CNT5, -+ }, { -+ /* 7: Half line HSYNC */ -+ .run_count = h_total / 2 - 1, -+ .run_src = DI_SYNC_CLK, -+ } -+ }; -+ -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ -+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); -+} -+ -+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig, int div) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* PIN2: HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync, -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN3: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* 4: Line Active */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = DI_SYNC_VSYNC, -+ } , { -+ /* 5: Pixel Active, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 5, /* Line Active */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ }, -+ }; -+ /* can't use #7 and #8 for line active and pixel active counters */ -+ struct di_sync_config cfg_vga[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* 2: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ } , { -+ /* 3: Line Active */ -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = 3 /* VSYNC */, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* 5: Pixel Active signal to DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 4, /* Line Active */ -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* unused */ -+ }, -+ }; -+ -+ ipu_di_write(di, v_total - 1, DI_SCR_CONF); -+ if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ else -+ ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); -+} -+ -+static void ipu_di_config_clock(struct ipu_di *di, -+ const struct ipu_di_signal_cfg *sig) -+{ -+ struct clk *clk; -+ unsigned clkgen0; -+ uint32_t val; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -+ /* -+ * CLKMODE_EXT means we must use the DI clock: this is -+ * needed for things like LVDS which needs to feed the -+ * DI and LDB with the same pixel clock. -+ */ -+ clk = di->clk_di; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -+ /* -+ * CLKMODE_SYNC means that we want the DI to be -+ * clocked at the same rate as the parent clock. -+ * This is needed (eg) for LDB which needs to be -+ * fed with the same pixel clock. We assume that -+ * the LDB clock has already been set correctly. -+ */ -+ clkgen0 = 1 << 4; -+ } else { -+ /* -+ * We can use the divider. We should really have -+ * a flag here indicating whether the bridge can -+ * cope with a fractional divider or not. For the -+ * time being, let's go for simplicitly and -+ * reliability. -+ */ -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } else { -+ /* -+ * For other interfaces, we can arbitarily select between -+ * the DI specific clock and the internal IPU clock. See -+ * DI_GENERAL bit 20. We select the IPU clock if it can -+ * give us a clock rate within 1% of the requested frequency, -+ * otherwise we use the DI clock. -+ */ -+ unsigned long rate, clkrate; -+ unsigned div, error; -+ -+ clkrate = clk_get_rate(di->clk_ipu); -+ div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ rate = clkrate / div; -+ -+ error = rate / (sig->mode.pixelclock / 1000); -+ -+ dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -+ rate, div, (signed)(error - 1000) / 10, error % 10); -+ -+ /* Allow a 1% error */ -+ if (error < 1010 && error >= 990) { -+ clk = di->clk_ipu; -+ -+ clkgen0 = div << 4; -+ } else { -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk = di->clk_di; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } -+ -+ di->clk_di_pixel = clk; -+ -+ /* Set the divider */ -+ ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -+ -+ /* -+ * Set the high/low periods. Bits 24:16 give us the falling edge, -+ * and bits 8:0 give the rising edge. LSB is fraction, and is -+ * based on the divider above. We want a 50% duty cycle, so set -+ * the falling edge to be half the divider. -+ */ -+ ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -+ -+ /* Finally select the input clock */ -+ val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -+ if (clk == di->clk_di) -+ val |= DI_GEN_DI_CLK_EXT; -+ ipu_di_write(di, val, DI_GENERAL); -+ -+ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -+ sig->mode.pixelclock, -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ clk == di->clk_di ? "DI" : "IPU", -+ clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); -+} -+ -+/* -+ * This function is called to adjust a video mode to IPU restrictions. -+ * It is meant to be called from drm crtc mode_fixup() methods. -+ */ -+int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) -+{ -+ u32 diff; -+ -+ if (mode->vfront_porch >= 2) -+ return 0; -+ -+ diff = 2 - mode->vfront_porch; -+ -+ if (mode->vback_porch >= diff) { -+ mode->vfront_porch = 2; -+ mode->vback_porch -= diff; -+ } else if (mode->vsync_len > diff) { -+ mode->vfront_porch = 2; -+ mode->vsync_len = mode->vsync_len - diff; -+ } else { -+ dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -+ return -EINVAL; -+ } -+ -+ dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -+ -+static u32 ipu_di_gen_polarity(int pin) -+{ -+ switch (pin) { -+ case 1: -+ return DI_GEN_POLARITY_1; -+ case 2: -+ return DI_GEN_POLARITY_2; -+ case 3: -+ return DI_GEN_POLARITY_3; -+ case 4: -+ return DI_GEN_POLARITY_4; -+ case 5: -+ return DI_GEN_POLARITY_5; -+ case 6: -+ return DI_GEN_POLARITY_6; -+ case 7: -+ return DI_GEN_POLARITY_7; -+ case 8: -+ return DI_GEN_POLARITY_8; -+ } -+ return 0; -+} -+ -+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) -+{ -+ u32 reg; -+ u32 di_gen, vsync_cnt; -+ u32 div; -+ -+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -+ di->id, sig->mode.hactive, sig->mode.vactive); -+ -+ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ sig->mode.pixelclock); -+ -+ mutex_lock(&di_mutex); -+ -+ ipu_di_config_clock(di, sig); -+ -+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -+ div = div / 16; /* Now divider is integer portion */ -+ -+ /* Setup pixel clock timing */ -+ /* Down time is half of period */ -+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -+ -+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -+ -+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -+ di_gen |= DI_GEN_DI_VSYNC_EXT; -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -+ ipu_di_sync_config_interlaced(di, sig); -+ -+ /* set y_sel = 1 */ -+ di_gen |= 0x10000000; -+ -+ vsync_cnt = 3; -+ } else { -+ ipu_di_sync_config_noninterlaced(di, sig, div); -+ -+ vsync_cnt = 3; -+ if (di->id == 1) -+ /* -+ * TODO: change only for TVEv2, parallel display -+ * uses pin 2 / 3 -+ */ -+ if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -+ vsync_cnt = 6; -+ } -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -+ if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -+ -+ if (sig->clk_pol) -+ di_gen |= DI_GEN_POLARITY_DISP_CLK; -+ -+ ipu_di_write(di, di_gen, DI_GENERAL); -+ -+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -+ DI_SYNC_AS_GEN); -+ -+ reg = ipu_di_read(di, DI_POL); -+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -+ -+ if (sig->enable_pol) -+ reg |= DI_POL_DRDY_POLARITY_15; -+ if (sig->data_pol) -+ reg |= DI_POL_DRDY_DATA_POLARITY; -+ -+ ipu_di_write(di, reg, DI_POL); -+ -+ mutex_unlock(&di_mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -+ -+int ipu_di_enable(struct ipu_di *di) -+{ -+ int ret; -+ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ret = clk_prepare_enable(di->clk_di_pixel); -+ if (ret) -+ return ret; -+ -+ ipu_module_enable(di->ipu, di->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_enable); -+ -+int ipu_di_disable(struct ipu_di *di) -+{ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ipu_module_disable(di->ipu, di->module); -+ -+ clk_disable_unprepare(di->clk_di_pixel); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_disable); -+ -+int ipu_di_get_num(struct ipu_di *di) -+{ -+ return di->id; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get_num); -+ -+static DEFINE_MUTEX(ipu_di_lock); -+ -+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) -+{ -+ struct ipu_di *di; -+ -+ if (disp > 1) -+ return ERR_PTR(-EINVAL); -+ -+ di = ipu->di_priv[disp]; -+ -+ mutex_lock(&ipu_di_lock); -+ -+ if (di->inuse) { -+ di = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ -+ di->inuse = true; -+out: -+ mutex_unlock(&ipu_di_lock); -+ -+ return di; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get); -+ -+void ipu_di_put(struct ipu_di *di) -+{ -+ mutex_lock(&ipu_di_lock); -+ -+ di->inuse = false; -+ -+ mutex_unlock(&ipu_di_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_di_put); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, -+ u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_di *di; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -+ if (!di) -+ return -ENOMEM; -+ -+ ipu->di_priv[id] = di; -+ -+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -+ if (IS_ERR(di->clk_di)) -+ return PTR_ERR(di->clk_di); -+ -+ di->module = module; -+ di->id = id; -+ di->clk_ipu = clk_ipu; -+ di->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!di->base) -+ return -ENOMEM; -+ -+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -+ -+ dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -+ id, base, di->base); -+ di->inuse = false; -+ di->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_di_exit(struct ipu_soc *ipu, int id) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-dmfc.c -@@ -0,0 +1,214 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DMFC_RD_CHAN 0x0000 -+#define DMFC_WR_CHAN 0x0004 -+#define DMFC_WR_CHAN_DEF 0x0008 -+#define DMFC_DP_CHAN 0x000c -+#define DMFC_DP_CHAN_DEF 0x0010 -+#define DMFC_GENERAL1 0x0014 -+#define DMFC_GENERAL2 0x0018 -+#define DMFC_IC_CTRL 0x001c -+#define DMFC_WR_CHAN_ALT 0x0020 -+#define DMFC_WR_CHAN_DEF_ALT 0x0024 -+#define DMFC_DP_CHAN_ALT 0x0028 -+#define DMFC_DP_CHAN_DEF_ALT 0x002c -+#define DMFC_GENERAL1_ALT 0x0030 -+#define DMFC_STAT 0x0034 -+ -+#define DMFC_WR_CHAN_1_28 0 -+#define DMFC_WR_CHAN_2_41 8 -+#define DMFC_WR_CHAN_1C_42 16 -+#define DMFC_WR_CHAN_2C_43 24 -+ -+#define DMFC_DP_CHAN_5B_23 0 -+#define DMFC_DP_CHAN_5F_27 8 -+#define DMFC_DP_CHAN_6B_24 16 -+#define DMFC_DP_CHAN_6F_29 24 -+ -+struct dmfc_channel_data { -+ int ipu_channel; -+ unsigned long channel_reg; -+ unsigned long shift; -+ unsigned eot_shift; -+ unsigned max_fifo_lines; -+}; -+ -+static const struct dmfc_channel_data dmfcdata[] = { -+ { -+ .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5B_23, -+ .eot_shift = 20, -+ .max_fifo_lines = 3, -+ }, { -+ .ipu_channel = 24, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6B_24, -+ .eot_shift = 22, -+ .max_fifo_lines = 1, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5F_27, -+ .eot_shift = 21, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .channel_reg = DMFC_WR_CHAN, -+ .shift = DMFC_WR_CHAN_1_28, -+ .eot_shift = 16, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = 29, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6F_29, -+ .eot_shift = 23, -+ .max_fifo_lines = 1, -+ }, -+}; -+ -+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -+ -+struct ipu_dmfc_priv; -+ -+struct dmfc_channel { -+ unsigned slots; -+ struct ipu_soc *ipu; -+ struct ipu_dmfc_priv *priv; -+ const struct dmfc_channel_data *data; -+}; -+ -+struct ipu_dmfc_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -+ struct mutex mutex; -+ void __iomem *base; -+ int use_count; -+}; -+ -+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -+ -+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -+ -+void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ u32 dmfc_gen1; -+ -+ mutex_lock(&priv->mutex); -+ -+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -+ -+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -+ dmfc_gen1 |= 1 << dmfc->data->eot_shift; -+ else -+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -+ -+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -+ -+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) -+{ -+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -+ int i; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) -+ if (dmfcdata[i].ipu_channel == ipu_channel) -+ return &priv->channels[i]; -+ return ERR_PTR(-ENODEV); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_get); -+ -+void ipu_dmfc_put(struct dmfc_channel *dmfc) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_put); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk) -+{ -+ struct ipu_dmfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ mutex_init(&priv->mutex); -+ -+ ipu->dmfc_priv = priv; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -+ priv->channels[i].priv = priv; -+ priv->channels[i].ipu = ipu; -+ priv->channels[i].data = &dmfcdata[i]; -+ -+ if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -+ priv->channels[i].slots = 2; -+ } -+ -+ writel(0x00000050, priv->base + DMFC_WR_CHAN); -+ writel(0x00005654, priv->base + DMFC_DP_CHAN); -+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -+ writel(0x00000003, priv->base + DMFC_GENERAL1); -+ -+ return 0; -+} -+ -+void ipu_dmfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-dp.c -@@ -0,0 +1,357 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DP_SYNC 0 -+#define DP_ASYNC0 0x60 -+#define DP_ASYNC1 0xBC -+ -+#define DP_COM_CONF 0x0 -+#define DP_GRAPH_WIND_CTRL 0x0004 -+#define DP_FG_POS 0x0008 -+#define DP_CSC_A_0 0x0044 -+#define DP_CSC_A_1 0x0048 -+#define DP_CSC_A_2 0x004C -+#define DP_CSC_A_3 0x0050 -+#define DP_CSC_0 0x0054 -+#define DP_CSC_1 0x0058 -+ -+#define DP_COM_CONF_FG_EN (1 << 0) -+#define DP_COM_CONF_GWSEL (1 << 1) -+#define DP_COM_CONF_GWAM (1 << 2) -+#define DP_COM_CONF_GWCKE (1 << 3) -+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) -+#define DP_COM_CONF_CSC_DEF_OFFSET 8 -+#define DP_COM_CONF_CSC_DEF_FG (3 << 8) -+#define DP_COM_CONF_CSC_DEF_BG (2 << 8) -+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -+ -+#define IPUV3_NUM_FLOWS 3 -+ -+struct ipu_dp_priv; -+ -+struct ipu_dp { -+ u32 flow; -+ bool in_use; -+ bool foreground; -+ enum ipu_color_space in_cs; -+}; -+ -+struct ipu_flow { -+ struct ipu_dp foreground; -+ struct ipu_dp background; -+ enum ipu_color_space out_cs; -+ void __iomem *base; -+ struct ipu_dp_priv *priv; -+}; -+ -+struct ipu_dp_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ void __iomem *base; -+ struct ipu_flow flow[IPUV3_NUM_FLOWS]; -+ struct mutex mutex; -+ int use_count; -+}; -+ -+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -+ -+static inline struct ipu_flow *to_flow(struct ipu_dp *dp) -+{ -+ if (dp->foreground) -+ return container_of(dp, struct ipu_flow, foreground); -+ else -+ return container_of(dp, struct ipu_flow, background); -+} -+ -+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -+ u8 alpha, bool bg_chan) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ if (bg_chan) -+ reg &= ~DP_COM_CONF_GWSEL; -+ else -+ reg |= DP_COM_CONF_GWSEL; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ if (enable) { -+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -+ writel(reg | ((u32) alpha << 24), -+ flow->base + DP_GRAPH_WIND_CTRL); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } else { -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -+ -+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -+ -+static void ipu_dp_csc_init(struct ipu_flow *flow, -+ enum ipu_color_space in, -+ enum ipu_color_space out, -+ u32 place) -+{ -+ u32 reg; -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ -+ if (in == out) { -+ writel(reg, flow->base + DP_COM_CONF); -+ return; -+ } -+ -+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -+ writel(0x3d6 | (0x0000 << 16) | (2 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -+ flow->base + DP_CSC_1); -+ } else { -+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -+ writel(0x000 | (0x3e42 << 16) | (1 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -+ flow->base + DP_CSC_1); -+ } -+ -+ reg |= place; -+ -+ writel(reg, flow->base + DP_COM_CONF); -+} -+ -+int ipu_dp_setup_channel(struct ipu_dp *dp, -+ enum ipu_color_space in, -+ enum ipu_color_space out) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ dp->in_cs = in; -+ -+ if (!dp->foreground) -+ flow->out_cs = out; -+ -+ if (flow->foreground.in_cs == flow->background.in_cs) { -+ /* -+ * foreground and background are of same colorspace, put -+ * colorspace converter after combining unit. -+ */ -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -+ DP_COM_CONF_CSC_DEF_BOTH); -+ } else { -+ if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -+ flow->foreground.in_cs == flow->out_cs) -+ /* -+ * foreground identical to output, apply color -+ * conversion on background -+ */ -+ ipu_dp_csc_init(flow, flow->background.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -+ else -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -+ -+int ipu_dp_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable); -+ -+int ipu_dp_enable_channel(struct ipu_dp *dp) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ if (!dp->foreground) -+ return 0; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg |= DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -+ -+void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg, csc; -+ -+ dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ -+ if (!dp->foreground) -+ return; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ csc = reg & DP_COM_CONF_CSC_DEF_MASK; -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -+ reg |= DP_COM_CONF_CSC_DEF_BG; -+ -+ reg &= ~DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ writel(0, flow->base + DP_FG_POS); -+ ipu_srm_dp_update(priv->ipu, sync); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -+ -+void ipu_dp_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable); -+ -+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ struct ipu_dp *dp; -+ -+ if ((flow >> 1) >= IPUV3_NUM_FLOWS) -+ return ERR_PTR(-EINVAL); -+ -+ if (flow & 1) -+ dp = &priv->flow[flow >> 1].foreground; -+ else -+ dp = &priv->flow[flow >> 1].background; -+ -+ if (dp->in_use) -+ return ERR_PTR(-EBUSY); -+ -+ dp->in_use = true; -+ -+ return dp; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_get); -+ -+void ipu_dp_put(struct ipu_dp *dp) -+{ -+ dp->in_use = false; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_put); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_dp_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ priv->dev = dev; -+ priv->ipu = ipu; -+ -+ ipu->dp_priv = priv; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -+ priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.foreground = true; -+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -+ priv->flow[i].priv = priv; -+ } -+ -+ return 0; -+} -+ -+void ipu_dp_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-ic.c -@@ -0,0 +1,761 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+ -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/bitrev.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/sizes.h> -+#include "ipu-prv.h" -+ -+/* IC Register Offsets */ -+#define IC_CONF 0x0000 -+#define IC_PRP_ENC_RSC 0x0004 -+#define IC_PRP_VF_RSC 0x0008 -+#define IC_PP_RSC 0x000C -+#define IC_CMBP_1 0x0010 -+#define IC_CMBP_2 0x0014 -+#define IC_IDMAC_1 0x0018 -+#define IC_IDMAC_2 0x001C -+#define IC_IDMAC_3 0x0020 -+#define IC_IDMAC_4 0x0024 -+ -+/* IC Register Fields */ -+#define IC_CONF_PRPENC_EN (1 << 0) -+#define IC_CONF_PRPENC_CSC1 (1 << 1) -+#define IC_CONF_PRPENC_ROT_EN (1 << 2) -+#define IC_CONF_PRPVF_EN (1 << 8) -+#define IC_CONF_PRPVF_CSC1 (1 << 9) -+#define IC_CONF_PRPVF_CSC2 (1 << 10) -+#define IC_CONF_PRPVF_CMB (1 << 11) -+#define IC_CONF_PRPVF_ROT_EN (1 << 12) -+#define IC_CONF_PP_EN (1 << 16) -+#define IC_CONF_PP_CSC1 (1 << 17) -+#define IC_CONF_PP_CSC2 (1 << 18) -+#define IC_CONF_PP_CMB (1 << 19) -+#define IC_CONF_PP_ROT_EN (1 << 20) -+#define IC_CONF_IC_GLB_LOC_A (1 << 28) -+#define IC_CONF_KEY_COLOR_EN (1 << 29) -+#define IC_CONF_RWS_EN (1 << 30) -+#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -+ -+#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) -+#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) -+#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) -+#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) -+#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) -+#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) -+#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) -+#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) -+#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) -+#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 -+#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) -+#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 -+#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) -+#define IC_IDMAC_1_PP_ROT_OFFSET 17 -+#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) -+#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) -+#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -+ -+#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) -+#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 -+#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) -+#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 -+#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) -+#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -+ -+#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) -+#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 -+#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) -+#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 -+#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) -+#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -+ -+struct ic_task_regoffs { -+ u32 rsc; -+ u32 tpmem_csc[2]; -+}; -+ -+struct ic_task_bitfields { -+ u32 ic_conf_en; -+ u32 ic_conf_rot_en; -+ u32 ic_conf_cmb_en; -+ u32 ic_conf_csc1_en; -+ u32 ic_conf_csc2_en; -+ u32 ic_cmb_galpha_bit; -+}; -+ -+static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .rsc = IC_PRP_ENC_RSC, -+ .tpmem_csc = {0x2008, 0}, -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .rsc = IC_PRP_VF_RSC, -+ .tpmem_csc = {0x4028, 0x4040}, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .rsc = IC_PP_RSC, -+ .tpmem_csc = {0x6060, 0x6078}, -+ }, -+}; -+ -+static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .ic_conf_en = IC_CONF_PRPENC_EN, -+ .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -+ .ic_conf_cmb_en = 0, /* NA */ -+ .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -+ .ic_conf_csc2_en = 0, /* NA */ -+ .ic_cmb_galpha_bit = 0, /* NA */ -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .ic_conf_en = IC_CONF_PRPVF_EN, -+ .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -+ .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -+ .ic_cmb_galpha_bit = 0, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .ic_conf_en = IC_CONF_PP_EN, -+ .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PP_CMB, -+ .ic_conf_csc1_en = IC_CONF_PP_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PP_CSC2, -+ .ic_cmb_galpha_bit = 8, -+ }, -+}; -+ -+struct ipu_ic_priv; -+ -+struct ipu_ic { -+ enum ipu_ic_task task; -+ const struct ic_task_regoffs *reg; -+ const struct ic_task_bitfields *bit; -+ -+ struct ipu_ic_colorspace in_cs; -+ struct ipu_ic_colorspace g_in_cs; -+ struct ipu_ic_colorspace out_cs; -+ -+ bool graphics; -+ bool rotation; -+ bool in_use; -+ -+ struct ipu_ic_priv *priv; -+}; -+ -+struct ipu_ic_priv { -+ void __iomem *base; -+ void __iomem *tpmem_base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ int use_count; -+ int irt_use_count; -+ struct ipu_ic task[IC_NUM_TASKS]; -+}; -+ -+static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) -+{ -+ return readl(ic->priv->base + offset); -+} -+ -+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) -+{ -+ writel(value, ic->priv->base + offset); -+} -+ -+static int init_csc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int csc_index) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 __iomem *base; -+ const u16 (*c)[3]; -+ const u16 *a; -+ u32 param; -+ -+ base = (u32 __iomem *) -+ (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -+ -+ /* Cast to unsigned */ -+ c = (const u16 (*)[3])csc->params.coeff; -+ a = (const u16 *)csc->params.offset; -+ -+ param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -+ ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -+ (csc->params.sat << 10); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -+ ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -+ ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ return 0; -+} -+ -+static int calc_resize_coeffs(struct ipu_ic *ic, -+ u32 in_size, u32 out_size, -+ u32 *resize_coeff, -+ u32 *downsize_coeff) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 temp_size, temp_downsize; -+ -+ /* -+ * Input size cannot be more than 4096, and output size cannot -+ * be more than 1024 -+ */ -+ if (in_size > 4096) { -+ dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -+ return -EINVAL; -+ } -+ if (out_size > 1024) { -+ dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -+ return -EINVAL; -+ } -+ -+ /* Cannot downsize more than 4:1 */ -+ if ((out_size << 2) < in_size) { -+ dev_err(ipu->dev, "Unsupported downsize\n"); -+ return -EINVAL; -+ } -+ -+ /* Compute downsizing coefficient */ -+ temp_downsize = 0; -+ temp_size = in_size; -+ while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -+ (temp_downsize < 2)) { -+ temp_size >>= 1; -+ temp_downsize++; -+ } -+ *downsize_coeff = temp_downsize; -+ -+ /* -+ * compute resizing coefficient using the following equation: -+ * resize_coeff = M * (SI - 1) / (SO - 1) -+ * where M = 2^13, SI = input size, SO = output size -+ */ -+ *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -+ if (*resize_coeff >= 16384L) { -+ dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -+ *resize_coeff = 0x3FFF; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_task_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf |= ic->bit->ic_conf_en; -+ -+ if (ic->rotation) -+ ic_conf |= ic->bit->ic_conf_rot_en; -+ -+ if (ic->in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->graphics) { -+ ic_conf |= ic->bit->ic_conf_cmb_en; -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->g_in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc2_en; -+ } -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -+ -+void ipu_ic_task_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf &= ~(ic->bit->ic_conf_en | -+ ic->bit->ic_conf_csc1_en | -+ ic->bit->ic_conf_rot_en); -+ if (ic->bit->ic_conf_csc2_en) -+ ic_conf &= ~ic->bit->ic_conf_csc2_en; -+ if (ic->bit->ic_conf_cmb_en) -+ ic_conf &= ~ic->bit->ic_conf_cmb_en; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -+ -+int ipu_ic_task_graphics_init(struct ipu_ic *ic, -+ const struct ipu_ic_colorspace *g_in_cs, -+ bool galpha_en, u32 galpha, -+ bool colorkey_en, u32 colorkey) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_ic_csc csc2; -+ unsigned long flags; -+ u32 reg, ic_conf; -+ int ret = 0; -+ -+ if (ic->task == IC_TASK_ENCODER) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -+ struct ipu_ic_csc csc1; -+ -+ ret = ipu_ic_calc_csc(&csc1, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB); -+ if (ret) -+ goto unlock; -+ -+ /* need transparent CSC1 conversion */ -+ ret = init_csc(ic, &csc1, 0); -+ if (ret) -+ goto unlock; -+ } -+ -+ ic->g_in_cs = *g_in_cs; -+ csc2.in_cs = ic->g_in_cs; -+ csc2.out_cs = ic->out_cs; -+ -+ ret = __ipu_ic_calc_csc(&csc2); -+ if (ret) -+ goto unlock; -+ -+ ret = init_csc(ic, &csc2, 1); -+ if (ret) -+ goto unlock; -+ -+ if (galpha_en) { -+ ic_conf |= IC_CONF_IC_GLB_LOC_A; -+ reg = ipu_ic_read(ic, IC_CMBP_1); -+ reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -+ reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -+ ipu_ic_write(ic, reg, IC_CMBP_1); -+ } else -+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -+ -+ if (colorkey_en) { -+ ic_conf |= IC_CONF_KEY_COLOR_EN; -+ ipu_ic_write(ic, colorkey, IC_CMBP_2); -+ } else -+ ic_conf &= ~IC_CONF_KEY_COLOR_EN; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ ic->graphics = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -+ -+int ipu_ic_task_init_rsc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height, -+ u32 rsc) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 downsize_coeff, resize_coeff; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!rsc) { -+ /* Setup vertical resizing */ -+ -+ ret = calc_resize_coeffs(ic, in_height, out_height, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc = (downsize_coeff << 30) | (resize_coeff << 16); -+ -+ /* Setup horizontal resizing */ -+ ret = calc_resize_coeffs(ic, in_width, out_width, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc |= (downsize_coeff << 14) | resize_coeff; -+ } -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ipu_ic_write(ic, rsc, ic->reg->rsc); -+ -+ /* Setup color space conversion */ -+ ic->in_cs = csc->in_cs; -+ ic->out_cs = csc->out_cs; -+ -+ ret = init_csc(ic, csc, 0); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+ -+int ipu_ic_task_init(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height) -+{ -+ return ipu_ic_task_init_rsc(ic, csc, -+ in_width, in_height, -+ out_width, out_height, 0); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_init); -+ -+int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -+ u32 width, u32 height, int burst_size, -+ enum ipu_rotate_mode rot) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -+ u32 temp_rot = bitrev8(rot) >> 5; -+ bool need_hor_flip = false; -+ unsigned long flags; -+ int ret = 0; -+ -+ if ((burst_size != 8) && (burst_size != 16)) { -+ dev_err(ipu->dev, "Illegal burst length for IC\n"); -+ return -EINVAL; -+ } -+ -+ width--; -+ height--; -+ -+ if (temp_rot & 0x2) /* Need horizontal flip */ -+ need_hor_flip = true; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -+ ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -+ ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -+ -+ switch (channel->num) { -+ case IPUV3_CHANNEL_IC_PP_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_PP: -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_ENC: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_VF_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_VF: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -+ break; -+ case IPUV3_CHANNEL_VDI_MEM_IC_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -+ break; -+ default: -+ goto unlock; -+ } -+ -+ ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -+ ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -+ ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -+ -+ if (ipu_rot_mode_is_irt(rot)) -+ ic->rotation = true; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -+ -+static void ipu_irt_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (!priv->irt_use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -+ -+ priv->irt_use_count++; -+} -+ -+static void ipu_irt_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (priv->irt_use_count) { -+ if (!--priv->irt_use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -+ } -+} -+ -+int ipu_ic_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -+ -+ priv->use_count++; -+ -+ if (ic->rotation) -+ ipu_irt_enable(ic); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_enable); -+ -+int ipu_ic_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ if (ic->rotation) -+ ipu_irt_disable(ic); -+ -+ ic->rotation = ic->graphics = false; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_disable); -+ -+struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) -+{ -+ struct ipu_ic_priv *priv = ipu->ic_priv; -+ unsigned long flags; -+ struct ipu_ic *ic, *ret; -+ -+ if (task >= IC_NUM_TASKS) -+ return ERR_PTR(-EINVAL); -+ -+ ic = &priv->task[task]; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (ic->in_use) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ ic->in_use = true; -+ ret = ic; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_get); -+ -+void ipu_ic_put(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ ic->in_use = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_put); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base) -+{ -+ struct ipu_ic_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->ic_priv = priv; -+ -+ spin_lock_init(&priv->lock); -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -+ if (!priv->tpmem_base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -+ -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ priv->task[i].task = i; -+ priv->task[i].priv = priv; -+ priv->task[i].reg = &ic_task_reg[i]; -+ priv->task[i].bit = &ic_task_bit[i]; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_exit(struct ipu_soc *ipu) -+{ -+} -+ -+void ipu_ic_dump(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ -+ dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CONF)); -+ dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_ENC_RSC)); -+ dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_VF_RSC)); -+ dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PP_RSC)); -+ dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_1)); -+ dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_1)); -+ dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_3)); -+ dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_4)); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-image-convert.c -@@ -0,0 +1,2475 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * -+ * Queued image conversion support, with tiling and rotation. -+ */ -+ -+#include <linux/interrupt.h> -+#include <linux/dma-mapping.h> -+#include <video/imx-ipu-image-convert.h> -+#include "ipu-prv.h" -+ -+/* -+ * The IC Resizer has a restriction that the output frame from the -+ * resizer must be 1024 or less in both width (pixels) and height -+ * (lines). -+ * -+ * The image converter attempts to split up a conversion when -+ * the desired output (converted) frame resolution exceeds the -+ * IC resizer limit of 1024 in either dimension. -+ * -+ * If either dimension of the output frame exceeds the limit, the -+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum -+ * of 4*4 or 16 tiles. A conversion is then carried out for each -+ * tile (but taking care to pass the full frame stride length to -+ * the DMA channel's parameter memory!). IDMA double-buffering is used -+ * to convert each tile back-to-back when possible (see note below -+ * when double_buffering boolean is set). -+ * -+ * Note that the input frame must be split up into the same number -+ * of tiles as the output frame: -+ * -+ * +---------+-----+ -+ * +-----+---+ | A | B | -+ * | A | B | | | | -+ * +-----+---+ --> +---------+-----+ -+ * | C | D | | C | D | -+ * +-----+---+ | | | -+ * +---------+-----+ -+ * -+ * Clockwise 90° rotations are handled by first rescaling into a -+ * reusable temporary tile buffer and then rotating with the 8x8 -+ * block rotator, writing to the correct destination: -+ * -+ * +-----+-----+ -+ * | | | -+ * +-----+---+ +---------+ | C | A | -+ * | A | B | | A,B, | | | | | -+ * +-----+---+ --> | C,D | | --> | | | -+ * | C | D | +---------+ +-----+-----+ -+ * +-----+---+ | D | B | -+ * | | | -+ * +-----+-----+ -+ * -+ * If the 8x8 block rotator is used, horizontal or vertical flipping -+ * is done during the rotation step, otherwise flipping is done -+ * during the scaling step. -+ * With rotation or flipping, tile order changes between input and -+ * output image. Tiles are numbered row major from top left to bottom -+ * right for both input and output image. -+ */ -+ -+#define MAX_STRIPES_W 4 -+#define MAX_STRIPES_H 4 -+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -+ -+#define MIN_W 16 -+#define MIN_H 8 -+#define MAX_W 4096 -+#define MAX_H 4096 -+ -+enum ipu_image_convert_type { -+ IMAGE_CONVERT_IN = 0, -+ IMAGE_CONVERT_OUT, -+}; -+ -+struct ipu_image_convert_dma_buf { -+ void *virt; -+ dma_addr_t phys; -+ unsigned long len; -+}; -+ -+struct ipu_image_convert_dma_chan { -+ int in; -+ int out; -+ int rot_in; -+ int rot_out; -+ int vdi_in_p; -+ int vdi_in; -+ int vdi_in_n; -+}; -+ -+/* dimensions of one tile */ -+struct ipu_image_tile { -+ u32 width; -+ u32 height; -+ u32 left; -+ u32 top; -+ /* size and strides are in bytes */ -+ u32 size; -+ u32 stride; -+ u32 rot_stride; -+ /* start Y or packed offset of this tile */ -+ u32 offset; -+ /* offset from start to tile in U plane, for planar formats */ -+ u32 u_off; -+ /* offset from start to tile in V plane, for planar formats */ -+ u32 v_off; -+}; -+ -+struct ipu_image_convert_image { -+ struct ipu_image base; -+ enum ipu_image_convert_type type; -+ -+ const struct ipu_image_pixfmt *fmt; -+ unsigned int stride; -+ -+ /* # of rows (horizontal stripes) if dest height is > 1024 */ -+ unsigned int num_rows; -+ /* # of columns (vertical stripes) if dest width is > 1024 */ -+ unsigned int num_cols; -+ -+ struct ipu_image_tile tile[MAX_TILES]; -+}; -+ -+struct ipu_image_pixfmt { -+ u32 fourcc; /* V4L2 fourcc */ -+ int bpp; /* total bpp */ -+ int uv_width_dec; /* decimation in width for U/V planes */ -+ int uv_height_dec; /* decimation in height for U/V planes */ -+ bool planar; /* planar format */ -+ bool uv_swapped; /* U and V planes are swapped */ -+ bool uv_packed; /* partial planar (U and V in same plane) */ -+}; -+ -+struct ipu_image_convert_ctx; -+struct ipu_image_convert_chan; -+struct ipu_image_convert_priv; -+ -+struct ipu_image_convert_ctx { -+ struct ipu_image_convert_chan *chan; -+ -+ ipu_image_convert_cb_t complete; -+ void *complete_context; -+ -+ /* Source/destination image data and rotation mode */ -+ struct ipu_image_convert_image in; -+ struct ipu_image_convert_image out; -+ struct ipu_ic_csc csc; -+ enum ipu_rotate_mode rot_mode; -+ u32 downsize_coeff_h; -+ u32 downsize_coeff_v; -+ u32 image_resize_coeff_h; -+ u32 image_resize_coeff_v; -+ u32 resize_coeffs_h[MAX_STRIPES_W]; -+ u32 resize_coeffs_v[MAX_STRIPES_H]; -+ -+ /* intermediate buffer for rotation */ -+ struct ipu_image_convert_dma_buf rot_intermediate[2]; -+ -+ /* current buffer number for double buffering */ -+ int cur_buf_num; -+ -+ bool aborting; -+ struct completion aborted; -+ -+ /* can we use double-buffering for this conversion operation? */ -+ bool double_buffering; -+ /* num_rows * num_cols */ -+ unsigned int num_tiles; -+ /* next tile to process */ -+ unsigned int next_tile; -+ /* where to place converted tile in dest image */ -+ unsigned int out_tile_map[MAX_TILES]; -+ -+ struct list_head list; -+}; -+ -+struct ipu_image_convert_chan { -+ struct ipu_image_convert_priv *priv; -+ -+ enum ipu_ic_task ic_task; -+ const struct ipu_image_convert_dma_chan *dma_ch; -+ -+ struct ipu_ic *ic; -+ struct ipuv3_channel *in_chan; -+ struct ipuv3_channel *out_chan; -+ struct ipuv3_channel *rotation_in_chan; -+ struct ipuv3_channel *rotation_out_chan; -+ -+ /* the IPU end-of-frame irqs */ -+ int out_eof_irq; -+ int rot_out_eof_irq; -+ -+ spinlock_t irqlock; -+ -+ /* list of convert contexts */ -+ struct list_head ctx_list; -+ /* queue of conversion runs */ -+ struct list_head pending_q; -+ /* queue of completed runs */ -+ struct list_head done_q; -+ -+ /* the current conversion run */ -+ struct ipu_image_convert_run *current_run; -+}; -+ -+struct ipu_image_convert_priv { -+ struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -+ struct ipu_soc *ipu; -+}; -+ -+static const struct ipu_image_convert_dma_chan -+image_convert_dma_chan[IC_NUM_TASKS] = { -+ [IC_TASK_VIEWFINDER] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -+ .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -+ .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -+ .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -+ .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -+ .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PP, -+ .out = IPUV3_CHANNEL_IC_PP_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -+ .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt image_convert_formats[] = { -+ { -+ .fourcc = V4L2_PIX_FMT_RGB565, -+ .bpp = 16, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XRGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XBGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGRX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGBX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_swapped = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_packed = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV422P, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV16, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ .uv_packed = true, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt *get_format(u32 fourcc) -+{ -+ const struct ipu_image_pixfmt *ret = NULL; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -+ if (image_convert_formats[i].fourcc == fourcc) { -+ ret = &image_convert_formats[i]; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static void dump_format(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -+ chan->ic_task, ctx, -+ ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -+ ic_image->base.pix.width, ic_image->base.pix.height, -+ ic_image->num_cols, ic_image->num_rows, -+ ic_image->fmt->fourcc & 0xff, -+ (ic_image->fmt->fourcc >> 8) & 0xff, -+ (ic_image->fmt->fourcc >> 16) & 0xff, -+ (ic_image->fmt->fourcc >> 24) & 0xff); -+} -+ -+int ipu_image_convert_enum_format(int index, u32 *fourcc) -+{ -+ const struct ipu_image_pixfmt *fmt; -+ -+ if (index >= (int)ARRAY_SIZE(image_convert_formats)) -+ return -EINVAL; -+ -+ /* Format found */ -+ fmt = &image_convert_formats[index]; -+ *fourcc = fmt->fourcc; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -+ -+static void free_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf) -+{ -+ if (buf->virt) -+ dma_free_coherent(priv->ipu->dev, -+ buf->len, buf->virt, buf->phys); -+ buf->virt = NULL; -+ buf->phys = 0; -+} -+ -+static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf, -+ int size) -+{ -+ buf->len = PAGE_ALIGN(size); -+ buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -+ GFP_DMA | GFP_KERNEL); -+ if (!buf->virt) { -+ dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static inline int num_stripes(int dim) -+{ -+ return (dim - 1) / 1024 + 1; -+} -+ -+/* -+ * Calculate downsizing coefficients, which are the same for all tiles, -+ * and initial bilinear resizing coefficients, which are used to find the -+ * best seam positions. -+ * Also determine the number of tiles necessary to guarantee that no tile -+ * is larger than 1024 pixels in either dimension at the output and between -+ * IC downsizing and main processing sections. -+ */ -+static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image *in, -+ struct ipu_image *out) -+{ -+ u32 downsized_width = in->rect.width; -+ u32 downsized_height = in->rect.height; -+ u32 downsize_coeff_v = 0; -+ u32 downsize_coeff_h = 0; -+ u32 resized_width = out->rect.width; -+ u32 resized_height = out->rect.height; -+ u32 resize_coeff_h; -+ u32 resize_coeff_v; -+ u32 cols; -+ u32 rows; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ resized_width = out->rect.height; -+ resized_height = out->rect.width; -+ } -+ -+ /* Do not let invalid input lead to an endless loop below */ -+ if (WARN_ON(resized_width == 0 || resized_height == 0)) -+ return -EINVAL; -+ -+ while (downsized_width >= resized_width * 2) { -+ downsized_width >>= 1; -+ downsize_coeff_h++; -+ } -+ -+ while (downsized_height >= resized_height * 2) { -+ downsized_height >>= 1; -+ downsize_coeff_v++; -+ } -+ -+ /* -+ * Calculate the bilinear resizing coefficients that could be used if -+ * we were converting with a single tile. The bottom right output pixel -+ * should sample as close as possible to the bottom right input pixel -+ * out of the decimator, but not overshoot it: -+ */ -+ resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -+ resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -+ -+ /* -+ * Both the output of the IC downsizing section before being passed to -+ * the IC main processing section and the final output of the IC main -+ * processing section must be <= 1024 pixels in both dimensions. -+ */ -+ cols = num_stripes(max_t(u32, downsized_width, resized_width)); -+ rows = num_stripes(max_t(u32, downsized_height, resized_height)); -+ -+ dev_dbg(ctx->chan->priv->ipu->dev, -+ "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -+ __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -+ resize_coeff_v, cols, rows); -+ -+ if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -+ resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -+ return -EINVAL; -+ -+ ctx->downsize_coeff_h = downsize_coeff_h; -+ ctx->downsize_coeff_v = downsize_coeff_v; -+ ctx->image_resize_coeff_h = resize_coeff_h; -+ ctx->image_resize_coeff_v = resize_coeff_v; -+ ctx->in.num_cols = cols; -+ ctx->in.num_rows = rows; -+ -+ return 0; -+} -+ -+#define round_closest(x, y) round_down((x) + (y)/2, (y)) -+ -+/* -+ * Find the best aligned seam position for the given column / row index. -+ * Rotation and image offsets are out of scope. -+ * -+ * @index: column / row index, used to calculate valid interval -+ * @in_edge: input right / bottom edge -+ * @out_edge: output right / bottom edge -+ * @in_align: input alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format -+ * @out_align: output alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format or rotator -+ * block size -+ * @in_burst: horizontal input burst size in case of horizontal flip -+ * @out_burst: horizontal output burst size or rotator block size -+ * @downsize_coeff: downsizing section coefficient -+ * @resize_coeff: main processing section resizing coefficient -+ * @_in_seam: aligned input seam position return value -+ * @_out_seam: aligned output seam position return value -+ */ -+static void find_best_seam(struct ipu_image_convert_ctx *ctx, -+ unsigned int index, -+ unsigned int in_edge, -+ unsigned int out_edge, -+ unsigned int in_align, -+ unsigned int out_align, -+ unsigned int in_burst, -+ unsigned int out_burst, -+ unsigned int downsize_coeff, -+ unsigned int resize_coeff, -+ u32 *_in_seam, -+ u32 *_out_seam) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int out_pos; -+ /* Input / output seam position candidates */ -+ unsigned int out_seam = 0; -+ unsigned int in_seam = 0; -+ unsigned int min_diff = UINT_MAX; -+ unsigned int out_start; -+ unsigned int out_end; -+ unsigned int in_start; -+ unsigned int in_end; -+ -+ /* Start within 1024 pixels of the right / bottom edge */ -+ out_start = max_t(int, index * out_align, out_edge - 1024); -+ /* End before having to add more columns to the left / rows above */ -+ out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -+ -+ /* -+ * Limit input seam position to make sure that the downsized input tile -+ * to the right or bottom does not exceed 1024 pixels. -+ */ -+ in_start = max_t(int, index * in_align, -+ in_edge - (1024 << downsize_coeff)); -+ in_end = min_t(unsigned int, in_edge, -+ index * (1024 << downsize_coeff) + 1); -+ -+ /* -+ * Output tiles must start at a multiple of 8 bytes horizontally and -+ * possibly at an even line horizontally depending on the pixel format. -+ * Only consider output aligned positions for the seam. -+ */ -+ out_start = round_up(out_start, out_align); -+ for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -+ unsigned int in_pos; -+ unsigned int in_pos_aligned; -+ unsigned int in_pos_rounded; -+ unsigned int abs_diff; -+ -+ /* -+ * Tiles in the right row / bottom column may not be allowed to -+ * overshoot horizontally / vertically. out_burst may be the -+ * actual DMA burst size, or the rotator block size. -+ */ -+ if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -+ continue; -+ -+ /* -+ * Input sample position, corresponding to out_pos, 19.13 fixed -+ * point. -+ */ -+ in_pos = (out_pos * resize_coeff) << downsize_coeff; -+ /* -+ * The closest input sample position that we could actually -+ * start the input tile at, 19.13 fixed point. -+ */ -+ in_pos_aligned = round_closest(in_pos, 8192U * in_align); -+ /* Convert 19.13 fixed point to integer */ -+ in_pos_rounded = in_pos_aligned / 8192U; -+ -+ if (in_pos_rounded < in_start) -+ continue; -+ if (in_pos_rounded >= in_end) -+ break; -+ -+ if ((in_burst > 1) && -+ (in_edge - in_pos_rounded) % in_burst) -+ continue; -+ -+ if (in_pos < in_pos_aligned) -+ abs_diff = in_pos_aligned - in_pos; -+ else -+ abs_diff = in_pos - in_pos_aligned; -+ -+ if (abs_diff < min_diff) { -+ in_seam = in_pos_rounded; -+ out_seam = out_pos; -+ min_diff = abs_diff; -+ } -+ } -+ -+ *_out_seam = out_seam; -+ *_in_seam = in_seam; -+ -+ dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -+ __func__, out_seam, out_align, out_start, out_end, -+ in_seam, in_align, in_start, in_end, min_diff / 8192, -+ DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); -+} -+ -+/* -+ * Tile left edges are required to be aligned to multiples of 8 bytes -+ * by the IDMAC. -+ */ -+static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) -+{ -+ if (fmt->planar) -+ return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -+ else -+ return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; -+} -+ -+/* -+ * Tile top edge alignment is only limited by chroma subsampling. -+ */ -+static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) -+{ -+ return fmt->uv_height_dec > 1 ? 2 : 1; -+} -+ -+static inline u32 tile_width_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN) { -+ /* -+ * The IC burst reads 8 pixels at a time. Reading beyond the -+ * end of the line is usually acceptable. Those pixels are -+ * ignored, unless the IC has to write the scaled line in -+ * reverse. -+ */ -+ return (!ipu_rot_mode_is_irt(rot_mode) && -+ (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -+ } -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (ipu_rot_mode_is_irt(rot_mode) && -+ fmt->planar && !fmt->uv_packed) ? -+ 8 * fmt->uv_width_dec : 8; -+} -+ -+static inline u32 tile_height_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -+ return 2; -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; -+} -+ -+/* -+ * Fill in left position and width and for all tiles in an input column, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a row, and output tile top position and height are set. -+ */ -+static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -+ unsigned int col, -+ struct ipu_image_convert_image *in, -+ unsigned int in_left, unsigned int in_width, -+ struct ipu_image_convert_image *out, -+ unsigned int out_left, unsigned int out_width) -+{ -+ unsigned int row, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (row = 0; row < in->num_rows; row++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->left = in_left; -+ in_tile->width = in_width; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->top = out_left; -+ out_tile->height = out_width; -+ } else { -+ out_tile->left = out_left; -+ out_tile->width = out_width; -+ } -+ } -+} -+ -+/* -+ * Fill in top position and height and for all tiles in an input row, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a column, and output tile left position and width are set. -+ */ -+static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -+ struct ipu_image_convert_image *in, -+ unsigned int in_top, unsigned int in_height, -+ struct ipu_image_convert_image *out, -+ unsigned int out_top, unsigned int out_height) -+{ -+ unsigned int col, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (col = 0; col < in->num_cols; col++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->top = in_top; -+ in_tile->height = in_height; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->left = out_top; -+ out_tile->width = out_height; -+ } else { -+ out_tile->top = out_top; -+ out_tile->height = out_height; -+ } -+ } -+} -+ -+/* -+ * Find the best horizontal and vertical seam positions to split into tiles. -+ * Minimize the fractional part of the input sampling position for the -+ * top / left pixels of each tile. -+ */ -+static void find_seams(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *in, -+ struct ipu_image_convert_image *out) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int resized_width = out->base.rect.width; -+ unsigned int resized_height = out->base.rect.height; -+ unsigned int col; -+ unsigned int row; -+ unsigned int in_left_align = tile_left_align(in->fmt); -+ unsigned int in_top_align = tile_top_align(in->fmt); -+ unsigned int out_left_align = tile_left_align(out->fmt); -+ unsigned int out_top_align = tile_top_align(out->fmt); -+ unsigned int out_width_align = tile_width_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int out_height_align = tile_height_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int in_right = in->base.rect.width; -+ unsigned int in_bottom = in->base.rect.height; -+ unsigned int out_right = out->base.rect.width; -+ unsigned int out_bottom = out->base.rect.height; -+ unsigned int flipped_out_left; -+ unsigned int flipped_out_top; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* Switch width/height and align top left to IRT block size */ -+ resized_width = out->base.rect.height; -+ resized_height = out->base.rect.width; -+ out_left_align = out_height_align; -+ out_top_align = out_width_align; -+ out_width_align = out_left_align; -+ out_height_align = out_top_align; -+ out_right = out->base.rect.height; -+ out_bottom = out->base.rect.width; -+ } -+ -+ for (col = in->num_cols - 1; col > 0; col--) { -+ bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ bool allow_out_overshoot = (col < in->num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ unsigned int in_left; -+ unsigned int out_left; -+ -+ /* -+ * Align input width to burst length if the scaling step flips -+ * horizontally. -+ */ -+ -+ find_best_seam(ctx, col, -+ in_right, out_right, -+ in_left_align, out_left_align, -+ allow_in_overshoot ? 1 : 8 /* burst length */, -+ allow_out_overshoot ? 1 : out_width_align, -+ ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -+ &in_left, &out_left); -+ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ flipped_out_left = resized_width - out_right; -+ else -+ flipped_out_left = out_left; -+ -+ fill_tile_column(ctx, col, in, in_left, in_right - in_left, -+ out, flipped_out_left, out_right - out_left); -+ -+ dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -+ in_left, in_right - in_left, -+ flipped_out_left, out_right - out_left); -+ -+ in_right = in_left; -+ out_right = out_left; -+ } -+ -+ flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -+ resized_width - out_right : 0; -+ -+ fill_tile_column(ctx, 0, in, 0, in_right, -+ out, flipped_out_left, out_right); -+ -+ dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -+ in_right, flipped_out_left, out_right); -+ -+ for (row = in->num_rows - 1; row > 0; row--) { -+ bool allow_overshoot = row < in->num_rows - 1; -+ unsigned int in_top; -+ unsigned int out_top; -+ -+ find_best_seam(ctx, row, -+ in_bottom, out_bottom, -+ in_top_align, out_top_align, -+ 1, allow_overshoot ? 1 : out_height_align, -+ ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -+ &in_top, &out_top); -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = out_top; -+ -+ fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -+ out, flipped_out_top, out_bottom - out_top); -+ -+ dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -+ in_top, in_bottom - in_top, -+ flipped_out_top, out_bottom - out_top); -+ -+ in_bottom = in_top; -+ out_bottom = out_top; -+ } -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = 0; -+ -+ fill_tile_row(ctx, 0, in, 0, in_bottom, -+ out, flipped_out_top, out_bottom); -+ -+ dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -+ in_bottom, flipped_out_top, out_bottom); -+} -+ -+static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned int max_width = 1024; -+ unsigned int max_height = 1024; -+ unsigned int i; -+ -+ if (image->type == IMAGE_CONVERT_IN) { -+ /* Up to 4096x4096 input tile size */ -+ max_width <<= ctx->downsize_coeff_h; -+ max_height <<= ctx->downsize_coeff_v; -+ } -+ -+ for (i = 0; i < ctx->num_tiles; i++) { -+ struct ipu_image_tile *tile; -+ const unsigned int row = i / image->num_cols; -+ const unsigned int col = i % image->num_cols; -+ -+ if (image->type == IMAGE_CONVERT_OUT) -+ tile = &image->tile[ctx->out_tile_map[i]]; -+ else -+ tile = &image->tile[i]; -+ -+ tile->size = ((tile->height * image->fmt->bpp) >> 3) * -+ tile->width; -+ -+ if (image->fmt->planar) { -+ tile->stride = tile->width; -+ tile->rot_stride = tile->height; -+ } else { -+ tile->stride = -+ (image->fmt->bpp * tile->width) >> 3; -+ tile->rot_stride = -+ (image->fmt->bpp * tile->height) >> 3; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -+ row, col, -+ tile->width, tile->height, tile->left, tile->top); -+ -+ if (!tile->width || tile->width > max_width || -+ !tile->height || tile->height > max_height) { -+ dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -+ image->type == IMAGE_CONVERT_IN ? "input" : -+ "output", tile->width, tile->height); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Use the rotation transformation to find the tile coordinates -+ * (row, col) of a tile in the destination frame that corresponds -+ * to the given tile coordinates of a source frame. The destination -+ * coordinate is then converted to a tile index. -+ */ -+static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -+ int src_row, int src_col) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ int dst_row, dst_col; -+ -+ /* with no rotation it's a 1:1 mapping */ -+ if (ctx->rot_mode == IPU_ROTATE_NONE) -+ return src_row * s_image->num_cols + src_col; -+ -+ /* -+ * before doing the transform, first we have to translate -+ * source row,col for an origin in the center of s_image -+ */ -+ src_row = src_row * 2 - (s_image->num_rows - 1); -+ src_col = src_col * 2 - (s_image->num_cols - 1); -+ -+ /* do the rotation transform */ -+ if (ctx->rot_mode & IPU_ROT_BIT_90) { -+ dst_col = -src_row; -+ dst_row = src_col; -+ } else { -+ dst_col = src_col; -+ dst_row = src_row; -+ } -+ -+ /* apply flip */ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ dst_col = -dst_col; -+ if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -+ dst_row = -dst_row; -+ -+ dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -+ chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -+ -+ /* -+ * finally translate dest row,col using an origin in upper -+ * left of d_image -+ */ -+ dst_row += d_image->num_rows - 1; -+ dst_col += d_image->num_cols - 1; -+ dst_row /= 2; -+ dst_col /= 2; -+ -+ return dst_row * d_image->num_cols + dst_col; -+} -+ -+/* -+ * Fill the out_tile_map[] with transformed destination tile indeces. -+ */ -+static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ unsigned int row, col, tile = 0; -+ -+ for (row = 0; row < s_image->num_rows; row++) { -+ for (col = 0; col < s_image->num_cols; col++) { -+ ctx->out_tile_map[tile] = -+ transform_tile_index(ctx, row, col); -+ tile++; -+ } -+ } -+} -+ -+static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 H, top, y_stride, uv_stride; -+ u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -+ u32 y_row_off, y_col_off, y_off; -+ u32 y_size, uv_size; -+ -+ /* setup some convenience vars */ -+ H = image->base.pix.height; -+ -+ y_stride = image->stride; -+ uv_stride = y_stride / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_stride *= 2; -+ -+ y_size = H * y_stride; -+ uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -+ -+ for (row = 0; row < image->num_rows; row++) { -+ top = image->tile[tile].top; -+ y_row_off = top * y_stride; -+ uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ y_col_off = image->tile[tile].left; -+ uv_col_off = y_col_off / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_col_off *= 2; -+ -+ y_off = y_row_off + y_col_off; -+ uv_off = uv_row_off + uv_col_off; -+ -+ u_off = y_size - y_off + uv_off; -+ v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -+ if (fmt->uv_swapped) { -+ tmp = u_off; -+ u_off = v_off; -+ v_off = tmp; -+ } -+ -+ image->tile[tile].offset = y_off; -+ image->tile[tile].u_off = u_off; -+ image->tile[tile++].v_off = v_off; -+ -+ if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "y_off %08x, u_off %08x, v_off %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ y_off, u_off, v_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 bpp, stride, offset; -+ u32 row_off, col_off; -+ -+ /* setup some convenience vars */ -+ stride = image->stride; -+ bpp = fmt->bpp; -+ -+ for (row = 0; row < image->num_rows; row++) { -+ row_off = image->tile[tile].top * stride; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ col_off = (image->tile[tile].left * bpp) >> 3; -+ -+ offset = row_off + col_off; -+ -+ image->tile[tile].offset = offset; -+ image->tile[tile].u_off = 0; -+ image->tile[tile++].v_off = 0; -+ -+ if (offset & 0x7) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "phys %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ row_off + col_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ if (image->fmt->planar) -+ return calc_tile_offsets_planar(ctx, image); -+ -+ return calc_tile_offsets_packed(ctx, image); -+} -+ -+/* -+ * Calculate the resizing ratio for the IC main processing section given input -+ * size, fixed downsizing coefficient, and output size. -+ * Either round to closest for the next tile's first pixel to minimize seams -+ * and distortion (for all but right column / bottom row), or round down to -+ * avoid sampling beyond the edges of the input image for this tile's last -+ * pixel. -+ * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -+ */ -+static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -+ u32 output_size, bool allow_overshoot) -+{ -+ u32 downsized = input_size >> downsize_coeff; -+ -+ if (allow_overshoot) -+ return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -+ else -+ return 8192 * (downsized - 1) / (output_size - 1); -+} -+ -+/* -+ * Slightly modify resize coefficients per tile to hide the bilinear -+ * interpolator reset at tile borders, shifting the right / bottom edge -+ * by up to a half input pixel. This removes noticeable seams between -+ * tiles at higher upscaling factors. -+ */ -+static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_tile *in_tile, *out_tile; -+ unsigned int col, row, tile_idx; -+ unsigned int last_output; -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ bool closest = (col < ctx->in.num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ u32 resized_width; -+ u32 resize_coeff_h; -+ u32 in_width; -+ -+ tile_idx = col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_width = out_tile->height; -+ else -+ resized_width = out_tile->width; -+ -+ resize_coeff_h = calc_resize_coeff(in_tile->width, -+ ctx->downsize_coeff_h, -+ resized_width, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -+ __func__, col, resize_coeff_h); -+ -+ /* -+ * With the horizontal scaling factor known, round up resized -+ * width (output width or height) to burst size. -+ */ -+ resized_width = round_up(resized_width, 8); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized width and scaling coefficients. Round up to -+ * burst size. -+ */ -+ last_output = resized_width - 1; -+ if (closest && ((last_output * resize_coeff_h) % 8192)) -+ last_output++; -+ in_width = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -+ << ctx->downsize_coeff_h, 8); -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->height = resized_width; -+ else -+ out_tile->width = resized_width; -+ -+ in_tile->width = in_width; -+ } -+ -+ ctx->resize_coeffs_h[col] = resize_coeff_h; -+ } -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ bool closest = (row < ctx->in.num_rows - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -+ u32 resized_height; -+ u32 resize_coeff_v; -+ u32 in_height; -+ -+ tile_idx = row * ctx->in.num_cols; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_height = out_tile->width; -+ else -+ resized_height = out_tile->height; -+ -+ resize_coeff_v = calc_resize_coeff(in_tile->height, -+ ctx->downsize_coeff_v, -+ resized_height, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -+ __func__, row, resize_coeff_v); -+ -+ /* -+ * With the vertical scaling factor known, round up resized -+ * height (output width or height) to IDMAC limitations. -+ */ -+ resized_height = round_up(resized_height, 2); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized height and scaling coefficients. Align to -+ * IDMAC restrictions. -+ */ -+ last_output = resized_height - 1; -+ if (closest && ((last_output * resize_coeff_v) % 8192)) -+ last_output++; -+ in_height = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -+ << ctx->downsize_coeff_v, 2); -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->width = resized_height; -+ else -+ out_tile->height = resized_height; -+ -+ in_tile->height = in_height; -+ } -+ -+ ctx->resize_coeffs_v[row] = resize_coeff_v; -+ } -+} -+ -+/* -+ * return the number of runs in given queue (pending_q or done_q) -+ * for this context. hold irqlock when calling. -+ */ -+static int get_run_count(struct ipu_image_convert_ctx *ctx, -+ struct list_head *q) -+{ -+ struct ipu_image_convert_run *run; -+ int count = 0; -+ -+ lockdep_assert_held(&ctx->chan->irqlock); -+ -+ list_for_each_entry(run, q, list) { -+ if (run->ctx == ctx) -+ count++; -+ } -+ -+ return count; -+} -+ -+static void convert_stop(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -+ __func__, chan->ic_task, ctx, run); -+ -+ /* disable IC tasks and the channels */ -+ ipu_ic_task_disable(chan->ic); -+ ipu_idmac_disable_channel(chan->in_chan); -+ ipu_idmac_disable_channel(chan->out_chan); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_disable_channel(chan->rotation_in_chan); -+ ipu_idmac_disable_channel(chan->rotation_out_chan); -+ ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -+ } -+ -+ ipu_ic_disable(chan->ic); -+} -+ -+static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -+ struct ipuv3_channel *channel, -+ struct ipu_image_convert_image *image, -+ enum ipu_rotate_mode rot_mode, -+ bool rot_swap_width_height, -+ unsigned int tile) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ unsigned int burst_size; -+ u32 width, height, stride; -+ dma_addr_t addr0, addr1 = 0; -+ struct ipu_image tile_image; -+ unsigned int tile_idx[2]; -+ -+ if (image->type == IMAGE_CONVERT_OUT) { -+ tile_idx[0] = ctx->out_tile_map[tile]; -+ tile_idx[1] = ctx->out_tile_map[1]; -+ } else { -+ tile_idx[0] = tile; -+ tile_idx[1] = 1; -+ } -+ -+ if (rot_swap_width_height) { -+ width = image->tile[tile_idx[0]].height; -+ height = image->tile[tile_idx[0]].width; -+ stride = image->tile[tile_idx[0]].rot_stride; -+ addr0 = ctx->rot_intermediate[0].phys; -+ if (ctx->double_buffering) -+ addr1 = ctx->rot_intermediate[1].phys; -+ } else { -+ width = image->tile[tile_idx[0]].width; -+ height = image->tile[tile_idx[0]].height; -+ stride = image->stride; -+ addr0 = image->base.phys0 + -+ image->tile[tile_idx[0]].offset; -+ if (ctx->double_buffering) -+ addr1 = image->base.phys0 + -+ image->tile[tile_idx[1]].offset; -+ } -+ -+ ipu_cpmem_zero(channel); -+ -+ memset(&tile_image, 0, sizeof(tile_image)); -+ tile_image.pix.width = tile_image.rect.width = width; -+ tile_image.pix.height = tile_image.rect.height = height; -+ tile_image.pix.bytesperline = stride; -+ tile_image.pix.pixelformat = image->fmt->fourcc; -+ tile_image.phys0 = addr0; -+ tile_image.phys1 = addr1; -+ if (image->fmt->planar && !rot_swap_width_height) { -+ tile_image.u_offset = image->tile[tile_idx[0]].u_off; -+ tile_image.v_offset = image->tile[tile_idx[0]].v_off; -+ } -+ -+ ipu_cpmem_set_image(channel, &tile_image); -+ -+ if (rot_mode) -+ ipu_cpmem_set_rotation(channel, rot_mode); -+ -+ /* -+ * Skip writing U and V components to odd rows in the output -+ * channels for planar 4:2:0. -+ */ -+ if ((channel == chan->out_chan || -+ channel == chan->rotation_out_chan) && -+ image->fmt->planar && image->fmt->uv_height_dec == 2) -+ ipu_cpmem_skip_odd_chroma_rows(channel); -+ -+ if (channel == chan->rotation_in_chan || -+ channel == chan->rotation_out_chan) { -+ burst_size = 8; -+ ipu_cpmem_set_block_mode(channel); -+ } else -+ burst_size = (width % 16) ? 8 : 16; -+ -+ ipu_cpmem_set_burstsize(channel, burst_size); -+ -+ ipu_ic_task_idma_init(chan->ic, channel, width, height, -+ burst_size, rot_mode); -+ -+ /* -+ * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -+ * only do this when there is no PRG present. -+ */ -+ if (!channel->ipu->prg_priv) -+ ipu_cpmem_set_axi_id(channel, 1); -+ -+ ipu_idmac_set_double_buffer(channel, ctx->double_buffering); -+} -+ -+static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ unsigned int dst_tile = ctx->out_tile_map[tile]; -+ unsigned int dest_width, dest_height; -+ unsigned int col, row; -+ u32 rsc; -+ int ret; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -+ __func__, chan->ic_task, ctx, run, tile, dst_tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* swap width/height for resizer */ -+ dest_width = d_image->tile[dst_tile].height; -+ dest_height = d_image->tile[dst_tile].width; -+ } else { -+ dest_width = d_image->tile[dst_tile].width; -+ dest_height = d_image->tile[dst_tile].height; -+ } -+ -+ row = tile / s_image->num_cols; -+ col = tile % s_image->num_cols; -+ -+ rsc = (ctx->downsize_coeff_v << 30) | -+ (ctx->resize_coeffs_v[row] << 16) | -+ (ctx->downsize_coeff_h << 14) | -+ (ctx->resize_coeffs_h[col]); -+ -+ dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -+ __func__, s_image->tile[tile].width, -+ s_image->tile[tile].height, dest_width, dest_height, rsc); -+ -+ /* setup the IC resizer and CSC */ -+ ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -+ s_image->tile[tile].width, -+ s_image->tile[tile].height, -+ dest_width, -+ dest_height, -+ rsc); -+ if (ret) { -+ dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -+ return ret; -+ } -+ -+ /* init the source MEM-->IC PP IDMAC channel */ -+ init_idmac_channel(ctx, chan->in_chan, s_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* init the IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ IPU_ROTATE_NONE, true, tile); -+ -+ /* init the MEM-->IC PP ROT IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -+ ctx->rot_mode, true, tile); -+ -+ /* init the destination IC PP ROT-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ /* now link IC PP-->MEM to MEM-->IC PP ROT */ -+ ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -+ } else { -+ /* init the destination IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ ctx->rot_mode, false, tile); -+ } -+ -+ /* enable the IC */ -+ ipu_ic_enable(chan->ic); -+ -+ /* set buffers ready */ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(chan->out_chan, 0); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -+ if (ctx->double_buffering) { -+ ipu_idmac_select_buffer(chan->in_chan, 1); -+ ipu_idmac_select_buffer(chan->out_chan, 1); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -+ } -+ -+ /* enable the channels! */ -+ ipu_idmac_enable_channel(chan->in_chan); -+ ipu_idmac_enable_channel(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_enable_channel(chan->rotation_in_chan); -+ ipu_idmac_enable_channel(chan->rotation_out_chan); -+ } -+ -+ ipu_ic_task_enable(chan->ic); -+ -+ ipu_cpmem_dump(chan->in_chan); -+ ipu_cpmem_dump(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_cpmem_dump(chan->rotation_in_chan); -+ ipu_cpmem_dump(chan->rotation_out_chan); -+ } -+ -+ ipu_dump(priv->ipu); -+ -+ return 0; -+} -+ -+/* hold irqlock when calling */ -+static int do_run(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ ctx->in.base.phys0 = run->in_phys; -+ ctx->out.base.phys0 = run->out_phys; -+ -+ ctx->cur_buf_num = 0; -+ ctx->next_tile = 1; -+ -+ /* remove run from pending_q and set as current */ -+ list_del(&run->list); -+ chan->current_run = run; -+ -+ return convert_start(run, 0); -+} -+ -+/* hold irqlock when calling */ -+static void run_next(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *tmp; -+ int ret; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ /* skip contexts that are aborting */ -+ if (run->ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: skipping aborting ctx %p run %p\n", -+ __func__, chan->ic_task, run->ctx, run); -+ continue; -+ } -+ -+ ret = do_run(run); -+ if (!ret) -+ break; -+ -+ /* -+ * something went wrong with start, add the run -+ * to done q and continue to the next run in the -+ * pending q. -+ */ -+ run->status = ret; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ } -+} -+ -+static void empty_done_q(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ while (!list_empty(&chan->done_q)) { -+ run = list_entry(chan->done_q.next, -+ struct ipu_image_convert_run, -+ list); -+ -+ list_del(&run->list); -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: completing ctx %p run %p with %d\n", -+ __func__, chan->ic_task, run->ctx, run, run->status); -+ -+ /* call the completion callback and free the run */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ run->ctx->complete(run, run->ctx->complete_context); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+} -+ -+/* -+ * the bottom half thread clears out the done_q, calling the -+ * completion handler for each. -+ */ -+static irqreturn_t do_bh(int irq, void *dev_id) -+{ -+ struct ipu_image_convert_chan *chan = dev_id; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -+ chan->ic_task); -+ -+ empty_done_q(chan); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* -+ * the done_q is cleared out, signal any contexts -+ * that are aborting that abort can complete. -+ */ -+ list_for_each_entry(ctx, &chan->ctx_list, list) { -+ if (ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: signaling abort for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ complete_all(&ctx->aborted); -+ } -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -+ chan->ic_task); -+ -+ return IRQ_HANDLED; -+} -+ -+static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) -+{ -+ unsigned int cur_tile = ctx->next_tile - 1; -+ unsigned int next_tile = ctx->next_tile; -+ -+ if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -+ ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -+ ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -+ ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -+ ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -+ ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -+ ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -+ ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -+ return true; -+ -+ return false; -+} -+ -+/* hold irqlock when calling */ -+static irqreturn_t do_irq(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_tile *src_tile, *dst_tile; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ struct ipuv3_channel *outch; -+ unsigned int dst_idx; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -+ chan->rotation_out_chan : chan->out_chan; -+ -+ /* -+ * It is difficult to stop the channel DMA before the channels -+ * enter the paused state. Without double-buffering the channels -+ * are always in a paused state when the EOF irq occurs, so it -+ * is safe to stop the channels now. For double-buffering we -+ * just ignore the abort until the operation completes, when it -+ * is safe to shut down. -+ */ -+ if (ctx->aborting && !ctx->double_buffering) { -+ convert_stop(run); -+ run->status = -EIO; -+ goto done; -+ } -+ -+ if (ctx->next_tile == ctx->num_tiles) { -+ /* -+ * the conversion is complete -+ */ -+ convert_stop(run); -+ run->status = 0; -+ goto done; -+ } -+ -+ /* -+ * not done, place the next tile buffers. -+ */ -+ if (!ctx->double_buffering) { -+ if (ic_settings_changed(ctx)) { -+ convert_stop(run); -+ convert_start(run, ctx->next_tile); -+ } else { -+ src_tile = &s_image->tile[ctx->next_tile]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, 0, -+ s_image->base.phys0 + -+ src_tile->offset); -+ ipu_cpmem_set_buffer(outch, 0, -+ d_image->base.phys0 + -+ dst_tile->offset); -+ if (s_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(chan->in_chan, -+ src_tile->u_off, -+ src_tile->v_off); -+ if (d_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(outch, -+ dst_tile->u_off, -+ dst_tile->v_off); -+ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(outch, 0); -+ } -+ } else if (ctx->next_tile < ctx->num_tiles - 1) { -+ -+ src_tile = &s_image->tile[ctx->next_tile + 1]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -+ s_image->base.phys0 + src_tile->offset); -+ ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -+ d_image->base.phys0 + dst_tile->offset); -+ -+ ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -+ ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -+ -+ ctx->cur_buf_num ^= 1; -+ } -+ -+ ctx->next_tile++; -+ return IRQ_HANDLED; -+done: -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t norotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this is a rotation operation, just ignore */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+static irqreturn_t rotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this was NOT a rotation operation, shouldn't happen */ -+ dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+/* -+ * try to force the completion of runs for this ctx. Called when -+ * abort wait times out in ipu_image_convert_abort(). -+ */ -+static void force_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ run = chan->current_run; -+ if (run && run->ctx == ctx) { -+ convert_stop(run); -+ run->status = -EIO; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ empty_done_q(chan); -+} -+ -+static void release_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ if (chan->out_eof_irq >= 0) -+ free_irq(chan->out_eof_irq, chan); -+ if (chan->rot_out_eof_irq >= 0) -+ free_irq(chan->rot_out_eof_irq, chan); -+ -+ if (!IS_ERR_OR_NULL(chan->in_chan)) -+ ipu_idmac_put(chan->in_chan); -+ if (!IS_ERR_OR_NULL(chan->out_chan)) -+ ipu_idmac_put(chan->out_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -+ ipu_idmac_put(chan->rotation_in_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -+ ipu_idmac_put(chan->rotation_out_chan); -+ if (!IS_ERR_OR_NULL(chan->ic)) -+ ipu_ic_put(chan->ic); -+ -+ chan->in_chan = chan->out_chan = chan->rotation_in_chan = -+ chan->rotation_out_chan = NULL; -+ chan->out_eof_irq = chan->rot_out_eof_irq = -1; -+} -+ -+static int get_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ int ret; -+ -+ /* get IC */ -+ chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -+ if (IS_ERR(chan->ic)) { -+ dev_err(priv->ipu->dev, "could not acquire IC\n"); -+ ret = PTR_ERR(chan->ic); -+ goto err; -+ } -+ -+ /* get IDMAC channels */ -+ chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -+ chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -+ if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -+ dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -+ chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -+ if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -+ dev_err(priv->ipu->dev, -+ "could not acquire idmac rotation channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ /* acquire the EOF interrupts */ -+ chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->out_eof_irq); -+ chan->out_eof_irq = -1; -+ goto err; -+ } -+ -+ chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->rotation_out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->rot_out_eof_irq); -+ chan->rot_out_eof_irq = -1; -+ goto err; -+ } -+ -+ return 0; -+err: -+ release_ipu_resources(chan); -+ return ret; -+} -+ -+static int fill_image(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image, -+ struct ipu_image *image, -+ enum ipu_image_convert_type type) -+{ -+ struct ipu_image_convert_priv *priv = ctx->chan->priv; -+ -+ ic_image->base = *image; -+ ic_image->type = type; -+ -+ ic_image->fmt = get_format(image->pix.pixelformat); -+ if (!ic_image->fmt) { -+ dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -+ type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -+ return -EINVAL; -+ } -+ -+ if (ic_image->fmt->planar) -+ ic_image->stride = ic_image->base.pix.width; -+ else -+ ic_image->stride = ic_image->base.pix.bytesperline; -+ -+ return 0; -+} -+ -+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ -+static unsigned int clamp_align(unsigned int x, unsigned int min, -+ unsigned int max, unsigned int align) -+{ -+ /* Bits that must be zero to be aligned */ -+ unsigned int mask = ~((1 << align) - 1); -+ -+ /* Clamp to aligned min and max */ -+ x = clamp(x, (min + ~mask) & mask, max & mask); -+ -+ /* Round to nearest aligned value */ -+ if (align) -+ x = (x + (1 << (align - 1))) & mask; -+ -+ return x; -+} -+ -+/* Adjusts input/output images to IPU restrictions */ -+void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ const struct ipu_image_pixfmt *infmt, *outfmt; -+ u32 w_align_out, h_align_out; -+ u32 w_align_in, h_align_in; -+ -+ infmt = get_format(in->pix.pixelformat); -+ outfmt = get_format(out->pix.pixelformat); -+ -+ /* set some default pixel formats if needed */ -+ if (!infmt) { -+ in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ infmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ if (!outfmt) { -+ out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ outfmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ -+ /* image converter does not handle fields */ -+ in->pix.field = out->pix.field = V4L2_FIELD_NONE; -+ -+ /* resizer cannot downsize more than 4:1 */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.width / 4); -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.height / 4); -+ } else { -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.width / 4); -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.height / 4); -+ } -+ -+ /* align input width/height */ -+ w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -+ w_align_in); -+ in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -+ h_align_in); -+ -+ /* align output width/height */ -+ w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -+ w_align_out); -+ out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -+ h_align_out); -+ -+ /* set input/output strides and image sizes */ -+ in->pix.bytesperline = infmt->planar ? -+ clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -+ w_align_in) : -+ clamp_align((in->pix.width * infmt->bpp) >> 3, -+ ((2 << w_align_in) * infmt->bpp) >> 3, -+ (MAX_W * infmt->bpp) >> 3, -+ w_align_in); -+ in->pix.sizeimage = infmt->planar ? -+ (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -+ in->pix.height * in->pix.bytesperline; -+ out->pix.bytesperline = outfmt->planar ? out->pix.width : -+ (out->pix.width * outfmt->bpp) >> 3; -+ out->pix.sizeimage = outfmt->planar ? -+ (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -+ out->pix.height * out->pix.bytesperline; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -+ -+/* -+ * this is used by ipu_image_convert_prepare() to verify set input and -+ * output images are valid before starting the conversion. Clients can -+ * also call it before calling ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image testin, testout; -+ -+ testin = *in; -+ testout = *out; -+ -+ ipu_image_convert_adjust(&testin, &testout, rot_mode); -+ -+ if (testin.pix.width != in->pix.width || -+ testin.pix.height != in->pix.height || -+ testout.pix.width != out->pix.width || -+ testout.pix.height != out->pix.height) -+ return -EINVAL; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -+ -+/* -+ * Call ipu_image_convert_prepare() to prepare for the conversion of -+ * given images and rotation mode. Returns a new conversion context. -+ */ -+struct ipu_image_convert_ctx * -+ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -+ struct ipu_image_convert_image *s_image, *d_image; -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ unsigned int i; -+ bool get_res; -+ int ret; -+ -+ if (!in || !out || !complete || -+ (ic_task != IC_TASK_VIEWFINDER && -+ ic_task != IC_TASK_POST_PROCESSOR)) -+ return ERR_PTR(-EINVAL); -+ -+ /* verify the in/out images before continuing */ -+ ret = ipu_image_convert_verify(in, out, rot_mode); -+ if (ret) { -+ dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -+ __func__); -+ return ERR_PTR(ret); -+ } -+ -+ chan = &priv->chan[ic_task]; -+ -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return ERR_PTR(-ENOMEM); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ ctx->chan = chan; -+ init_completion(&ctx->aborted); -+ -+ ctx->rot_mode = rot_mode; -+ -+ /* Sets ctx->in.num_rows/cols as well */ -+ ret = calc_image_resize_coefficients(ctx, in, out); -+ if (ret) -+ goto out_free; -+ -+ s_image = &ctx->in; -+ d_image = &ctx->out; -+ -+ /* set tiling and rotation */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ d_image->num_rows = s_image->num_cols; -+ d_image->num_cols = s_image->num_rows; -+ } else { -+ d_image->num_rows = s_image->num_rows; -+ d_image->num_cols = s_image->num_cols; -+ } -+ -+ ctx->num_tiles = d_image->num_cols * d_image->num_rows; -+ -+ ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -+ if (ret) -+ goto out_free; -+ ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -+ if (ret) -+ goto out_free; -+ -+ calc_out_tile_map(ctx); -+ -+ find_seams(ctx, s_image, d_image); -+ -+ ret = calc_tile_dimensions(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ ret = calc_tile_offsets(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_dimensions(ctx, d_image); -+ ret = calc_tile_offsets(ctx, d_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_resize_coefficients(ctx); -+ -+ ret = ipu_ic_calc_csc(&ctx->csc, -+ s_image->base.pix.ycbcr_enc, -+ s_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -+ d_image->base.pix.ycbcr_enc, -+ d_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -+ if (ret) -+ goto out_free; -+ -+ dump_format(ctx, s_image); -+ dump_format(ctx, d_image); -+ -+ ctx->complete = complete; -+ ctx->complete_context = complete_context; -+ -+ /* -+ * Can we use double-buffering for this operation? If there is -+ * only one tile (the whole image can be converted in a single -+ * operation) there's no point in using double-buffering. Also, -+ * the IPU's IDMAC channels allow only a single U and V plane -+ * offset shared between both buffers, but these offsets change -+ * for every tile, and therefore would have to be updated for -+ * each buffer which is not possible. So double-buffering is -+ * impossible when either the source or destination images are -+ * a planar format (YUV420, YUV422P, etc.). Further, differently -+ * sized tiles or different resizing coefficients per tile -+ * prevent double-buffering as well. -+ */ -+ ctx->double_buffering = (ctx->num_tiles > 1 && -+ !s_image->fmt->planar && -+ !d_image->fmt->planar); -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (ctx->in.tile[i].width != ctx->in.tile[0].width || -+ ctx->in.tile[i].height != ctx->in.tile[0].height || -+ ctx->out.tile[i].width != ctx->out.tile[0].width || -+ ctx->out.tile[i].height != ctx->out.tile[0].height) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_cols; i++) { -+ if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_rows; i++) { -+ if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ unsigned long intermediate_size = d_image->tile[0].size; -+ -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (d_image->tile[i].size > intermediate_size) -+ intermediate_size = d_image->tile[i].size; -+ } -+ -+ ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -+ intermediate_size); -+ if (ret) -+ goto out_free; -+ if (ctx->double_buffering) { -+ ret = alloc_dma_buf(priv, -+ &ctx->rot_intermediate[1], -+ intermediate_size); -+ if (ret) -+ goto out_free_dmabuf0; -+ } -+ } -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ get_res = list_empty(&chan->ctx_list); -+ -+ list_add_tail(&ctx->list, &chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (get_res) { -+ ret = get_ipu_resources(chan); -+ if (ret) -+ goto out_free_dmabuf1; -+ } -+ -+ return ctx; -+ -+out_free_dmabuf1: -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ list_del(&ctx->list); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+out_free_dmabuf0: -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+out_free: -+ kfree(ctx); -+ return ERR_PTR(ret); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -+ -+/* -+ * Carry out a single image conversion run. Only the physaddr's of the input -+ * and output image buffers are needed. The conversion context must have -+ * been created previously with ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_queue(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_priv *priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!run || !run->ctx || !run->in_phys || !run->out_phys) -+ return -EINVAL; -+ -+ ctx = run->ctx; -+ chan = ctx->chan; -+ priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -+ chan->ic_task, ctx, run); -+ -+ INIT_LIST_HEAD(&run->list); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ if (ctx->aborting) { -+ ret = -EIO; -+ goto unlock; -+ } -+ -+ list_add_tail(&run->list, &chan->pending_q); -+ -+ if (!chan->current_run) { -+ ret = do_run(run); -+ if (ret) -+ chan->current_run = NULL; -+ } -+unlock: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -+ -+/* Abort any active or pending conversions for this context */ -+static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *active_run, *tmp; -+ unsigned long flags; -+ int run_count, ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* move all remaining pending runs in this context to done_q */ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ if (run->ctx != ctx) -+ continue; -+ run->status = -EIO; -+ list_move_tail(&run->list, &chan->done_q); -+ } -+ -+ run_count = get_run_count(ctx, &chan->done_q); -+ active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -+ chan->current_run : NULL; -+ -+ if (active_run) -+ reinit_completion(&ctx->aborted); -+ -+ ctx->aborting = true; -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (!run_count && !active_run) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: no abort needed for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ return; -+ } -+ -+ if (!active_run) { -+ empty_done_q(chan); -+ return; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: wait for completion: %d runs\n", -+ __func__, chan->ic_task, run_count); -+ -+ ret = wait_for_completion_timeout(&ctx->aborted, -+ msecs_to_jiffies(10000)); -+ if (ret == 0) { -+ dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -+ force_abort(ctx); -+ } -+} -+ -+void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ __ipu_image_convert_abort(ctx); -+ ctx->aborting = false; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -+ -+/* Unprepare image conversion context */ -+void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned long flags; -+ bool put_res; -+ -+ /* make sure no runs are hanging around */ -+ __ipu_image_convert_abort(ctx); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ list_del(&ctx->list); -+ -+ put_res = list_empty(&chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (put_res) -+ release_ipu_resources(chan); -+ -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+ -+ kfree(ctx); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -+ -+/* -+ * "Canned" asynchronous single image conversion. Allocates and returns -+ * a new conversion run. On successful return the caller must free the -+ * run and call ipu_image_convert_unprepare() after conversion completes. -+ */ -+struct ipu_image_convert_run * -+ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ int ret; -+ -+ ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -+ complete, complete_context); -+ if (IS_ERR(ctx)) -+ return ERR_CAST(ctx); -+ -+ run = kzalloc(sizeof(*run), GFP_KERNEL); -+ if (!run) { -+ ipu_image_convert_unprepare(ctx); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ run->ctx = ctx; -+ run->in_phys = in->phys0; -+ run->out_phys = out->phys0; -+ -+ ret = ipu_image_convert_queue(run); -+ if (ret) { -+ ipu_image_convert_unprepare(ctx); -+ kfree(run); -+ return ERR_PTR(ret); -+ } -+ -+ return run; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert); -+ -+/* "Canned" synchronous single image conversion */ -+static void image_convert_sync_complete(struct ipu_image_convert_run *run, -+ void *data) -+{ -+ struct completion *comp = data; -+ -+ complete(comp); -+} -+ -+int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image_convert_run *run; -+ struct completion comp; -+ int ret; -+ -+ init_completion(&comp); -+ -+ run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -+ image_convert_sync_complete, &comp); -+ if (IS_ERR(run)) -+ return PTR_ERR(run); -+ -+ ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -+ ret = (ret == 0) ? -ETIMEDOUT : 0; -+ -+ ipu_image_convert_unprepare(run->ctx); -+ kfree(run); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) -+{ -+ struct ipu_image_convert_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->image_convert_priv = priv; -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ struct ipu_image_convert_chan *chan = &priv->chan[i]; -+ -+ chan->ic_task = i; -+ chan->priv = priv; -+ chan->dma_ch = &image_convert_dma_chan[i]; -+ chan->out_eof_irq = -1; -+ chan->rot_out_eof_irq = -1; -+ -+ spin_lock_init(&chan->irqlock); -+ INIT_LIST_HEAD(&chan->ctx_list); -+ INIT_LIST_HEAD(&chan->pending_q); -+ INIT_LIST_HEAD(&chan->done_q); -+ } -+ -+ return 0; -+} -+ -+void ipu_image_convert_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-pre.c -@@ -0,0 +1,346 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/genalloc.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRE_MAX_WIDTH 2048 -+#define IPU_PRE_NUM_SCANLINES 8 -+ -+#define IPU_PRE_CTRL 0x000 -+#define IPU_PRE_CTRL_SET 0x004 -+#define IPU_PRE_CTRL_ENABLE (1 << 0) -+#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) -+#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) -+#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) -+#define IPU_PRE_CTRL_VFLIP (1 << 5) -+#define IPU_PRE_CTRL_SO (1 << 6) -+#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) -+#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) -+#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) -+#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) -+#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) -+#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) -+#define IPU_PRE_CTRL_CLKGATE (1 << 30) -+#define IPU_PRE_CTRL_SFTRST (1 << 31) -+ -+#define IPU_PRE_CUR_BUF 0x030 -+ -+#define IPU_PRE_NEXT_BUF 0x040 -+ -+#define IPU_PRE_TPR_CTRL 0x070 -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6) -+ -+#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 -+#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) -+#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) -+#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) -+#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) -+#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) -+#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -+ -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 -+#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_CTRL 0x110 -+#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) -+#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+ -+#define IPU_PRE_STORE_ENG_STATUS 0x120 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -+ -+#define IPU_PRE_STORE_ENG_SIZE 0x130 -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_PITCH 0x140 -+#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -+ -+#define IPU_PRE_STORE_ENG_ADDR 0x150 -+ -+struct ipu_pre { -+ struct list_head list; -+ struct device *dev; -+ -+ void __iomem *regs; -+ struct clk *clk_axi; -+ struct gen_pool *iram; -+ -+ dma_addr_t buffer_paddr; -+ void *buffer_virt; -+ bool in_use; -+ unsigned int safe_window_end; -+ unsigned int last_bufaddr; -+}; -+ -+static DEFINE_MUTEX(ipu_pre_list_mutex); -+static LIST_HEAD(ipu_pre_list); -+static int available_pres; -+ -+int ipu_pre_get_available_count(void) -+{ -+ return available_pres; -+} -+ -+struct ipu_pre * -+ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) -+{ -+ struct device_node *pre_node = of_parse_phandle(dev->of_node, -+ name, index); -+ struct ipu_pre *pre; -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_for_each_entry(pre, &ipu_pre_list, list) { -+ if (pre_node == pre->dev->of_node) { -+ mutex_unlock(&ipu_pre_list_mutex); -+ device_link_add(dev, pre->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ of_node_put(pre_node); -+ return pre; -+ } -+ } -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ of_node_put(pre_node); -+ -+ return NULL; -+} -+ -+int ipu_pre_get(struct ipu_pre *pre) -+{ -+ u32 val; -+ -+ if (pre->in_use) -+ return -EBUSY; -+ -+ /* first get the engine out of reset and remove clock gating */ -+ writel(0, pre->regs + IPU_PRE_CTRL); -+ -+ /* init defaults that should be applied to all streams */ -+ val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -+ IPU_PRE_CTRL_HANDSHAKE_EN | -+ IPU_PRE_CTRL_TPR_REST_SEL | -+ IPU_PRE_CTRL_SDW_UPDATE; -+ writel(val, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = true; -+ return 0; -+} -+ -+void ipu_pre_put(struct ipu_pre *pre) -+{ -+ writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = false; -+} -+ -+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -+ unsigned int height, unsigned int stride, u32 format, -+ uint64_t modifier, unsigned int bufaddr) -+{ -+ const struct drm_format_info *info = drm_format_info(format); -+ u32 active_bpp = info->cpp[0] >> 1; -+ u32 val; -+ -+ /* calculate safe window for ctrl register updates */ -+ if (modifier == DRM_FORMAT_MOD_LINEAR) -+ pre->safe_window_end = height - 2; -+ else -+ pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1; -+ -+ writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); -+ writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -+ pre->last_bufaddr = bufaddr; -+ -+ val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -+ IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -+ IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -+ IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -+ -+ val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -+ IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -+ -+ val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -+ -+ val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -+ IPU_PRE_STORE_ENG_CTRL_STORE_EN; -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -+ -+ val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -+ IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -+ -+ val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -+ -+ writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); -+ -+ val = readl(pre->regs + IPU_PRE_TPR_CTRL); -+ val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK; -+ if (modifier != DRM_FORMAT_MOD_LINEAR) { -+ /* only support single buffer formats for now */ -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF; -+ if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED; -+ if (info->cpp[0] == 2) -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT; -+ } -+ writel(val, pre->regs + IPU_PRE_TPR_CTRL); -+ -+ val = readl(pre->regs + IPU_PRE_CTRL); -+ val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | -+ IPU_PRE_CTRL_SDW_UPDATE; -+ if (modifier == DRM_FORMAT_MOD_LINEAR) -+ val &= ~IPU_PRE_CTRL_BLOCK_EN; -+ else -+ val |= IPU_PRE_CTRL_BLOCK_EN; -+ writel(val, pre->regs + IPU_PRE_CTRL); -+} -+ -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) -+{ -+ unsigned long timeout = jiffies + msecs_to_jiffies(5); -+ unsigned short current_yblock; -+ u32 val; -+ -+ if (bufaddr == pre->last_bufaddr) -+ return; -+ -+ writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -+ pre->last_bufaddr = bufaddr; -+ -+ do { -+ if (time_after(jiffies, timeout)) { -+ dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -+ return; -+ } -+ -+ val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -+ current_yblock = -+ (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -+ IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -+ } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -+ -+ writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); -+} -+ -+bool ipu_pre_update_pending(struct ipu_pre *pre) -+{ -+ return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -+ IPU_PRE_CTRL_SDW_UPDATE); -+} -+ -+u32 ipu_pre_get_baddr(struct ipu_pre *pre) -+{ -+ return (u32)pre->buffer_paddr; -+} -+ -+static int ipu_pre_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_pre *pre; -+ -+ pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -+ if (!pre) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ pre->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(pre->regs)) -+ return PTR_ERR(pre->regs); -+ -+ pre->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(pre->clk_axi)) -+ return PTR_ERR(pre->clk_axi); -+ -+ pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -+ if (!pre->iram) -+ return -EPROBE_DEFER; -+ -+ /* -+ * Allocate IRAM buffer with maximum size. This could be made dynamic, -+ * but as there is no other user of this IRAM region and we can fit all -+ * max sized buffers into it, there is no need yet. -+ */ -+ pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -+ IPU_PRE_NUM_SCANLINES * 4, -+ &pre->buffer_paddr); -+ if (!pre->buffer_virt) -+ return -ENOMEM; -+ -+ clk_prepare_enable(pre->clk_axi); -+ -+ pre->dev = dev; -+ platform_set_drvdata(pdev, pre); -+ mutex_lock(&ipu_pre_list_mutex); -+ list_add(&pre->list, &ipu_pre_list); -+ available_pres++; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_pre_remove(struct platform_device *pdev) -+{ -+ struct ipu_pre *pre = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_del(&pre->list); -+ available_pres--; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ clk_disable_unprepare(pre->clk_axi); -+ -+ if (pre->buffer_virt) -+ gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -+ IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -+ return 0; -+} -+ -+static const struct of_device_id ipu_pre_dt_ids[] = { -+ { .compatible = "fsl,imx6qp-pre", }, -+ { /* sentinel */ }, -+}; -+ -+struct platform_driver ipu_pre_drv = { -+ .probe = ipu_pre_probe, -+ .remove = ipu_pre_remove, -+ .driver = { -+ .name = "imx-ipu-pre", -+ .of_match_table = ipu_pre_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-prg.c -@@ -0,0 +1,483 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/iopoll.h> -+#include <linux/mfd/syscon.h> -+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/pm_runtime.h> -+#include <linux/regmap.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRG_CTL 0x00 -+#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) -+#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 -+#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) -+#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) -+#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) -+#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) -+#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) -+#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) -+#define IPU_PRG_CTL_SOFTRST (1 << 30) -+#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -+ -+#define IPU_PRG_STATUS 0x04 -+#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) -+#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -+ -+#define IPU_PRG_QOS 0x08 -+#define IPU_PRG_QOS_ARID_MASK 0xf -+#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -+ -+#define IPU_PRG_REG_UPDATE 0x0c -+#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -+ -+#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) -+#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -+ -+#define IPU_PRG_CROP_LINE 0x1c -+ -+#define IPU_PRG_THD 0x20 -+ -+#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -+ -+#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -+ -+#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -+ -+#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -+ -+struct ipu_prg_channel { -+ bool enabled; -+ int used_pre; -+}; -+ -+struct ipu_prg { -+ struct list_head list; -+ struct device *dev; -+ int id; -+ -+ void __iomem *regs; -+ struct clk *clk_ipg, *clk_axi; -+ struct regmap *iomuxc_gpr; -+ struct ipu_pre *pres[3]; -+ -+ struct ipu_prg_channel chan[3]; -+}; -+ -+static DEFINE_MUTEX(ipu_prg_list_mutex); -+static LIST_HEAD(ipu_prg_list); -+ -+struct ipu_prg * -+ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) -+{ -+ struct device_node *prg_node = of_parse_phandle(dev->of_node, -+ name, 0); -+ struct ipu_prg *prg; -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_for_each_entry(prg, &ipu_prg_list, list) { -+ if (prg_node == prg->dev->of_node) { -+ mutex_unlock(&ipu_prg_list_mutex); -+ device_link_add(dev, prg->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ prg->id = ipu_id; -+ of_node_put(prg_node); -+ return prg; -+ } -+ } -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ of_node_put(prg_node); -+ -+ return NULL; -+} -+ -+int ipu_prg_max_active_channels(void) -+{ -+ return ipu_pre_get_available_count(); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -+ -+bool ipu_prg_present(struct ipu_soc *ipu) -+{ -+ if (ipu->prg_priv) -+ return true; -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_present); -+ -+bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -+ uint64_t modifier) -+{ -+ const struct drm_format_info *info = drm_format_info(format); -+ -+ if (info->num_planes != 1) -+ return false; -+ -+ switch (modifier) { -+ case DRM_FORMAT_MOD_LINEAR: -+ case DRM_FORMAT_MOD_VIVANTE_TILED: -+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: -+ return true; -+ default: -+ return false; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_prg_format_supported); -+ -+int ipu_prg_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_prg *prg = ipu->prg_priv; -+ -+ if (!prg) -+ return 0; -+ -+ return pm_runtime_get_sync(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_enable); -+ -+void ipu_prg_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_prg *prg = ipu->prg_priv; -+ -+ if (!prg) -+ return; -+ -+ pm_runtime_put(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_disable); -+ -+/* -+ * The channel configuartion functions below are not thread safe, as they -+ * must be only called from the atomic commit path in the DRM driver, which -+ * is properly serialized. -+ */ -+static int ipu_prg_ipu_to_prg_chan(int ipu_chan) -+{ -+ /* -+ * This isn't clearly documented in the RM, but IPU to PRG channel -+ * assignment is fixed, as only with this mapping the control signals -+ * match up. -+ */ -+ switch (ipu_chan) { -+ case IPUV3_CHANNEL_MEM_BG_SYNC: -+ return 0; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: -+ return 1; -+ case IPUV3_CHANNEL_MEM_DC_SYNC: -+ return 2; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ int i, ret; -+ -+ /* channel 0 is special as it is hardwired to one of the PREs */ -+ if (prg_chan == 0) { -+ ret = ipu_pre_get(prg->pres[0]); -+ if (ret) -+ goto fail; -+ prg->chan[prg_chan].used_pre = 0; -+ return 0; -+ } -+ -+ for (i = 1; i < 3; i++) { -+ ret = ipu_pre_get(prg->pres[i]); -+ if (!ret) { -+ u32 val, mux; -+ int shift; -+ -+ prg->chan[prg_chan].used_pre = i; -+ -+ /* configure the PRE to PRG channel mux */ -+ shift = (i == 1) ? 12 : 14; -+ mux = (prg->id << 1) | (prg_chan - 1); -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, mux << shift); -+ -+ /* check other mux, must not point to same channel */ -+ shift = (i == 1) ? 14 : 12; -+ regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -+ if (((val >> shift) & 0x3) == mux) { -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, -+ (mux ^ 0x1) << shift); -+ } -+ -+ return 0; -+ } -+ } -+ -+fail: -+ dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -+ return ret; -+} -+ -+static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -+ -+ ipu_pre_put(prg->pres[chan->used_pre]); -+ chan->used_pre = -1; -+} -+ -+void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ -+ if (prg_chan < 0) -+ return; -+ -+ chan = &prg->chan[prg_chan]; -+ if (!chan->enabled) -+ return; -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ pm_runtime_put(prg->dev); -+ -+ ipu_prg_put_pre(prg, prg_chan); -+ -+ chan->enabled = false; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); -+ -+int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, -+ unsigned int axi_id, unsigned int width, -+ unsigned int height, unsigned int stride, -+ u32 format, uint64_t modifier, unsigned long *eba) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ int ret; -+ -+ if (prg_chan < 0) -+ return prg_chan; -+ -+ chan = &prg->chan[prg_chan]; -+ -+ if (chan->enabled) { -+ ipu_pre_update(prg->pres[chan->used_pre], *eba); -+ return 0; -+ } -+ -+ ret = ipu_prg_get_pre(prg, prg_chan); -+ if (ret) -+ return ret; -+ -+ ipu_pre_configure(prg->pres[chan->used_pre], -+ width, height, stride, format, modifier, *eba); -+ -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -+ writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -+ -+ val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -+ ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -+ writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -+ -+ val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -+ *eba = val; -+ writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ /* config AXI ID */ -+ val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -+ IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -+ val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -+ /* enable channel */ -+ val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ /* wait for both double buffers to be filled */ -+ readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -+ (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -+ (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -+ 5, 1000); -+ -+ pm_runtime_put(prg->dev); -+ -+ chan->enabled = true; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -+ -+bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ -+ if (prg_chan < 0) -+ return false; -+ -+ chan = &prg->chan[prg_chan]; -+ WARN_ON(!chan->enabled); -+ -+ return ipu_pre_update_pending(prg->pres[chan->used_pre]); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -+ -+static int ipu_prg_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_prg *prg; -+ u32 val; -+ int i, ret; -+ -+ prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -+ if (!prg) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ prg->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(prg->regs)) -+ return PTR_ERR(prg->regs); -+ -+ -+ prg->clk_ipg = devm_clk_get(dev, "ipg"); -+ if (IS_ERR(prg->clk_ipg)) -+ return PTR_ERR(prg->clk_ipg); -+ -+ prg->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(prg->clk_axi)) -+ return PTR_ERR(prg->clk_axi); -+ -+ prg->iomuxc_gpr = -+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -+ if (IS_ERR(prg->iomuxc_gpr)) -+ return PTR_ERR(prg->iomuxc_gpr); -+ -+ for (i = 0; i < 3; i++) { -+ prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -+ if (!prg->pres[i]) -+ return -EPROBE_DEFER; -+ } -+ -+ ret = clk_prepare_enable(prg->clk_ipg); -+ if (ret) -+ return ret; -+ -+ ret = clk_prepare_enable(prg->clk_axi); -+ if (ret) { -+ clk_disable_unprepare(prg->clk_ipg); -+ return ret; -+ } -+ -+ /* init to free running mode */ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_SHADOW_EN; -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ /* disable address threshold */ -+ writel(0xffffffff, prg->regs + IPU_PRG_THD); -+ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ -+ prg->dev = dev; -+ platform_set_drvdata(pdev, prg); -+ mutex_lock(&ipu_prg_list_mutex); -+ list_add(&prg->list, &ipu_prg_list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_prg_remove(struct platform_device *pdev) -+{ -+ struct ipu_prg *prg = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_del(&prg->list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int prg_suspend(struct device *dev) -+{ -+ struct ipu_prg *prg = dev_get_drvdata(dev); -+ -+ clk_disable_unprepare(prg->clk_axi); -+ clk_disable_unprepare(prg->clk_ipg); -+ -+ return 0; -+} -+ -+static int prg_resume(struct device *dev) -+{ -+ struct ipu_prg *prg = dev_get_drvdata(dev); -+ int ret; -+ -+ ret = clk_prepare_enable(prg->clk_ipg); -+ if (ret) -+ return ret; -+ -+ ret = clk_prepare_enable(prg->clk_axi); -+ if (ret) { -+ clk_disable_unprepare(prg->clk_ipg); -+ return ret; -+ } -+ -+ return 0; -+} -+#endif -+ -+static const struct dev_pm_ops prg_pm_ops = { -+ SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) -+}; -+ -+static const struct of_device_id ipu_prg_dt_ids[] = { -+ { .compatible = "fsl,imx6qp-prg", }, -+ { /* sentinel */ }, -+}; -+ -+struct platform_driver ipu_prg_drv = { -+ .probe = ipu_prg_probe, -+ .remove = ipu_prg_remove, -+ .driver = { -+ .name = "imx-ipu-prg", -+ .pm = &prg_pm_ops, -+ .of_match_table = ipu_prg_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-prv.h -@@ -0,0 +1,274 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#ifndef __IPU_PRV_H__ -+#define __IPU_PRV_H__ -+ -+struct ipu_soc; -+ -+#include <linux/types.h> -+#include <linux/device.h> -+#include <linux/clk.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+ -+#define IPU_MCU_T_DEFAULT 8 -+#define IPU_CM_IDMAC_REG_OFS 0x00008000 -+#define IPU_CM_IC_REG_OFS 0x00020000 -+#define IPU_CM_IRT_REG_OFS 0x00028000 -+#define IPU_CM_CSI0_REG_OFS 0x00030000 -+#define IPU_CM_CSI1_REG_OFS 0x00038000 -+#define IPU_CM_SMFC_REG_OFS 0x00050000 -+#define IPU_CM_DC_REG_OFS 0x00058000 -+#define IPU_CM_DMFC_REG_OFS 0x00060000 -+ -+/* Register addresses */ -+/* IPU Common registers */ -+#define IPU_CM_REG(offset) (offset) -+ -+#define IPU_CONF IPU_CM_REG(0) -+ -+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) -+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) -+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) -+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) -+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) -+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) -+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) -+#define IPU_SKIP IPU_CM_REG(0x00bc) -+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) -+#define IPU_DISP_GEN IPU_CM_REG(0x00c4) -+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) -+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) -+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) -+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) -+#define IPU_SNOOP IPU_CM_REG(0x00d8) -+#define IPU_MEM_RST IPU_CM_REG(0x00dc) -+#define IPU_PM IPU_CM_REG(0x00e0) -+#define IPU_GPR IPU_CM_REG(0x00e4) -+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) -+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) -+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) -+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) -+#define IPU_SRM_STAT IPU_CM_REG(0x024C) -+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) -+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) -+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -+ -+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) -+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -+ -+/* SRM_PRI2 */ -+#define DP_S_SRM_MODE_MASK (0x3 << 3) -+#define DP_S_SRM_MODE_NOW (0x3 << 3) -+#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -+ -+/* FS_PROC_FLOW1 */ -+#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) -+#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) -+#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) -+#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) -+#define FS_PP_SRC_SEL_MASK (0xf << 12) -+#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) -+#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) -+#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) -+#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) -+#define FS_PRP_SRC_SEL_MASK (0xf << 24) -+#define FS_VDI_SRC_SEL_MASK (0x3 << 28) -+#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) -+#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -+ -+/* FS_PROC_FLOW2 */ -+#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) -+#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) -+#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) -+#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) -+#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) -+#define FS_PP_DEST_SEL_MASK (0xf << 12) -+#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) -+#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) -+#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) -+#define FS_PRP_DEST_SEL_MASK (0xf << 24) -+ -+#define IPU_DI0_COUNTER_RELEASE (1 << 24) -+#define IPU_DI1_COUNTER_RELEASE (1 << 25) -+ -+#define IPU_IDMAC_REG(offset) (offset) -+ -+#define IDMAC_CONF IPU_IDMAC_REG(0x0000) -+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) -+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) -+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) -+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) -+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) -+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) -+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) -+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) -+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) -+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) -+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) -+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -+ -+#define IPU_NUM_IRQS (32 * 15) -+ -+enum ipu_modules { -+ IPU_CONF_CSI0_EN = (1 << 0), -+ IPU_CONF_CSI1_EN = (1 << 1), -+ IPU_CONF_IC_EN = (1 << 2), -+ IPU_CONF_ROT_EN = (1 << 3), -+ IPU_CONF_ISP_EN = (1 << 4), -+ IPU_CONF_DP_EN = (1 << 5), -+ IPU_CONF_DI0_EN = (1 << 6), -+ IPU_CONF_DI1_EN = (1 << 7), -+ IPU_CONF_SMFC_EN = (1 << 8), -+ IPU_CONF_DC_EN = (1 << 9), -+ IPU_CONF_DMFC_EN = (1 << 10), -+ -+ IPU_CONF_VDI_EN = (1 << 12), -+ -+ IPU_CONF_IDMAC_DIS = (1 << 22), -+ -+ IPU_CONF_IC_DMFC_SEL = (1 << 25), -+ IPU_CONF_IC_DMFC_SYNC = (1 << 26), -+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -+ -+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -+ IPU_CONF_IC_INPUT = (1 << 30), -+ IPU_CONF_CSI_SEL = (1 << 31), -+}; -+ -+struct ipuv3_channel { -+ unsigned int num; -+ struct ipu_soc *ipu; -+ struct list_head list; -+}; -+ -+struct ipu_cpmem; -+struct ipu_csi; -+struct ipu_dc_priv; -+struct ipu_dmfc_priv; -+struct ipu_di; -+struct ipu_ic_priv; -+struct ipu_vdi; -+struct ipu_image_convert_priv; -+struct ipu_smfc_priv; -+struct ipu_pre; -+struct ipu_prg; -+ -+struct ipu_devtype; -+ -+struct ipu_soc { -+ struct device *dev; -+ const struct ipu_devtype *devtype; -+ enum ipuv3_type ipu_type; -+ spinlock_t lock; -+ struct mutex channel_lock; -+ struct list_head channels; -+ -+ void __iomem *cm_reg; -+ void __iomem *idmac_reg; -+ -+ int id; -+ int usecount; -+ -+ struct clk *clk; -+ -+ int irq_sync; -+ int irq_err; -+ struct irq_domain *domain; -+ -+ struct ipu_cpmem *cpmem_priv; -+ struct ipu_dc_priv *dc_priv; -+ struct ipu_dp_priv *dp_priv; -+ struct ipu_dmfc_priv *dmfc_priv; -+ struct ipu_di *di_priv[2]; -+ struct ipu_csi *csi_priv[2]; -+ struct ipu_ic_priv *ic_priv; -+ struct ipu_vdi *vdi_priv; -+ struct ipu_image_convert_priv *image_convert_priv; -+ struct ipu_smfc_priv *smfc_priv; -+ struct ipu_prg *prg_priv; -+}; -+ -+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->idmac_reg + offset); -+} -+ -+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -+ unsigned offset) -+{ -+ writel(value, ipu->idmac_reg + offset); -+} -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask); -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu); -+void ipu_csi_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base); -+void ipu_ic_exit(struct ipu_soc *ipu); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module); -+void ipu_vdi_exit(struct ipu_soc *ipu); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); -+void ipu_image_convert_exit(struct ipu_soc *ipu); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *ipu_clk); -+void ipu_di_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk); -+void ipu_dmfc_exit(struct ipu_soc *ipu); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_dp_exit(struct ipu_soc *ipu); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ unsigned long template_base); -+void ipu_dc_exit(struct ipu_soc *ipu); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_cpmem_exit(struct ipu_soc *ipu); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_smfc_exit(struct ipu_soc *ipu); -+ -+struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -+ int index); -+int ipu_pre_get_available_count(void); -+int ipu_pre_get(struct ipu_pre *pre); -+void ipu_pre_put(struct ipu_pre *pre); -+u32 ipu_pre_get_baddr(struct ipu_pre *pre); -+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -+ unsigned int height, unsigned int stride, u32 format, -+ uint64_t modifier, unsigned int bufaddr); -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); -+bool ipu_pre_update_pending(struct ipu_pre *pre); -+ -+struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -+ int ipu_id); -+ -+extern struct platform_driver ipu_pre_drv; -+extern struct platform_driver ipu_prg_drv; -+ -+#endif /* __IPU_PRV_H__ */ ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-smfc.c -@@ -0,0 +1,202 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/clk.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_smfc { -+ struct ipu_smfc_priv *priv; -+ int chno; -+ bool inuse; -+}; -+ -+struct ipu_smfc_priv { -+ void __iomem *base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ struct ipu_smfc channel[4]; -+ int use_count; -+}; -+ -+/*SMFC Registers */ -+#define SMFC_MAP 0x0000 -+#define SMFC_WMC 0x0004 -+#define SMFC_BS 0x0008 -+ -+int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 4; -+ val = readl(priv->base + SMFC_BS); -+ val &= ~(0xf << shift); -+ val |= burstsize << shift; -+ writel(val, priv->base + SMFC_BS); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -+ -+int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 3; -+ val = readl(priv->base + SMFC_MAP); -+ val &= ~(0x7 << shift); -+ val |= ((csi_id << 2) | mipi_id) << shift; -+ writel(val, priv->base + SMFC_MAP); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -+ -+int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -+ val = readl(priv->base + SMFC_WMC); -+ val &= ~(0x3f << shift); -+ val |= ((clr_level << 3) | set_level) << shift; -+ writel(val, priv->base + SMFC_WMC); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -+ -+int ipu_smfc_enable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ priv->use_count++; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_enable); -+ -+int ipu_smfc_disable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_disable); -+ -+struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) -+{ -+ struct ipu_smfc_priv *priv = ipu->smfc_priv; -+ struct ipu_smfc *smfc, *ret; -+ unsigned long flags; -+ -+ if (chno >= 4) -+ return ERR_PTR(-EINVAL); -+ -+ smfc = &priv->channel[chno]; -+ ret = smfc; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (smfc->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ smfc->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_get); -+ -+void ipu_smfc_put(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ smfc->inuse = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_put); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base) -+{ -+ struct ipu_smfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->smfc_priv = priv; -+ spin_lock_init(&priv->lock); -+ priv->ipu = ipu; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ for (i = 0; i < 4; i++) { -+ priv->channel[i].priv = priv; -+ priv->channel[i].chno = i; -+ } -+ -+ pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -+ -+ return 0; -+} -+ -+void ipu_smfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/imx/ipu-v3/ipu-vdi.c -@@ -0,0 +1,234 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/io.h> -+#include "ipu-prv.h" -+ -+struct ipu_vdi { -+ void __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+ -+/* VDI Register Offsets */ -+#define VDI_FSIZE 0x0000 -+#define VDI_C 0x0004 -+ -+/* VDI Register Fields */ -+#define VDI_C_CH_420 (0 << 1) -+#define VDI_C_CH_422 (1 << 1) -+#define VDI_C_MOT_SEL_MASK (0x3 << 2) -+#define VDI_C_MOT_SEL_FULL (2 << 2) -+#define VDI_C_MOT_SEL_LOW (1 << 2) -+#define VDI_C_MOT_SEL_MED (0 << 2) -+#define VDI_C_BURST_SIZE1_4 (3 << 4) -+#define VDI_C_BURST_SIZE2_4 (3 << 8) -+#define VDI_C_BURST_SIZE3_4 (3 << 12) -+#define VDI_C_BURST_SIZE_MASK 0xF -+#define VDI_C_BURST_SIZE1_OFFSET 4 -+#define VDI_C_BURST_SIZE2_OFFSET 8 -+#define VDI_C_BURST_SIZE3_OFFSET 12 -+#define VDI_C_VWM1_SET_1 (0 << 16) -+#define VDI_C_VWM1_SET_2 (1 << 16) -+#define VDI_C_VWM1_CLR_2 (1 << 19) -+#define VDI_C_VWM3_SET_1 (0 << 22) -+#define VDI_C_VWM3_SET_2 (1 << 22) -+#define VDI_C_VWM3_CLR_2 (1 << 25) -+#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) -+#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -+ -+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) -+{ -+ return readl(vdi->base + offset); -+} -+ -+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -+ unsigned int offset) -+{ -+ writel(value, vdi->base + offset); -+} -+ -+void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) -+{ -+ bool top_field_0 = false; -+ unsigned long flags; -+ u32 reg; -+ -+ switch (field) { -+ case V4L2_FIELD_INTERLACED_TB: -+ case V4L2_FIELD_SEQ_TB: -+ case V4L2_FIELD_TOP: -+ top_field_0 = true; -+ break; -+ case V4L2_FIELD_INTERLACED_BT: -+ case V4L2_FIELD_SEQ_BT: -+ case V4L2_FIELD_BOTTOM: -+ top_field_0 = false; -+ break; -+ default: -+ top_field_0 = (std & V4L2_STD_525_60) ? true : false; -+ break; -+ } -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ if (top_field_0) -+ reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -+ else -+ reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -+ -+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ -+ reg &= ~VDI_C_MOT_SEL_MASK; -+ -+ switch (motion_sel) { -+ case MED_MOTION: -+ reg |= VDI_C_MOT_SEL_MED; -+ break; -+ case HIGH_MOTION: -+ reg |= VDI_C_MOT_SEL_FULL; -+ break; -+ default: -+ reg |= VDI_C_MOT_SEL_LOW; -+ break; -+ } -+ -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -+ -+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) -+{ -+ unsigned long flags; -+ u32 pixel_fmt, reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ((yres - 1) << 16) | (xres - 1); -+ ipu_vdi_write(vdi, reg, VDI_FSIZE); -+ -+ /* -+ * Full motion, only vertical filter is used. -+ * Burst size is 4 accesses -+ */ -+ if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -+ code == MEDIA_BUS_FMT_UYVY8_1X16 || -+ code == MEDIA_BUS_FMT_YUYV8_2X8 || -+ code == MEDIA_BUS_FMT_YUYV8_1X16) -+ pixel_fmt = VDI_C_CH_422; -+ else -+ pixel_fmt = VDI_C_CH_420; -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ reg |= pixel_fmt; -+ reg |= VDI_C_BURST_SIZE2_4; -+ reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -+ reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_setup); -+ -+void ipu_vdi_unsetup(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ ipu_vdi_write(vdi, 0, VDI_FSIZE); -+ ipu_vdi_write(vdi, 0, VDI_C); -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -+ -+int ipu_vdi_enable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (!vdi->use_count) -+ ipu_module_enable(vdi->ipu, vdi->module); -+ -+ vdi->use_count++; -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_enable); -+ -+int ipu_vdi_disable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (vdi->use_count) { -+ if (!--vdi->use_count) -+ ipu_module_disable(vdi->ipu, vdi->module); -+ } -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_disable); -+ -+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) -+{ -+ return ipu->vdi_priv; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_get); -+ -+void ipu_vdi_put(struct ipu_vdi *vdi) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_put); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module) -+{ -+ struct ipu_vdi *vdi; -+ -+ vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -+ if (!vdi) -+ return -ENOMEM; -+ -+ ipu->vdi_priv = vdi; -+ -+ spin_lock_init(&vdi->lock); -+ vdi->module = module; -+ vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!vdi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -+ vdi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_vdi_exit(struct ipu_soc *ipu) -+{ -+} ---- a/drivers/gpu/ipu-v3/Kconfig -+++ /dev/null -@@ -1,11 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0-only --config IMX_IPUV3_CORE -- tristate "IPUv3 core support" -- depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST -- depends on DRM || !DRM # if DRM=m, this can't be 'y' -- select BITREVERSE -- select GENERIC_ALLOCATOR if DRM -- select GENERIC_IRQ_CHIP -- help -- Choose this if you have a i.MX5/6 system and want to use the Image -- Processing Unit. This option only enables IPU base support. ---- a/drivers/gpu/ipu-v3/Makefile -+++ /dev/null -@@ -1,10 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -- --imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -- ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -- ipu-image-convert.o ipu-smfc.o ipu-vdi.o -- --ifdef CONFIG_DRM -- imx-ipu-v3-objs += ipu-pre.o ipu-prg.o --endif ---- a/drivers/gpu/ipu-v3/ipu-common.c -+++ /dev/null -@@ -1,1565 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/module.h> --#include <linux/export.h> --#include <linux/types.h> --#include <linux/reset.h> --#include <linux/platform_device.h> --#include <linux/err.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> --#include <linux/clk.h> --#include <linux/list.h> --#include <linux/irq.h> --#include <linux/irqchip/chained_irq.h> --#include <linux/irqdomain.h> --#include <linux/of_device.h> --#include <linux/of_graph.h> -- --#include <drm/drm_fourcc.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->cm_reg + offset); --} -- --static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) --{ -- writel(value, ipu->cm_reg + offset); --} -- --int ipu_get_num(struct ipu_soc *ipu) --{ -- return ipu->id; --} --EXPORT_SYMBOL_GPL(ipu_get_num); -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) --{ -- u32 val; -- -- val = ipu_cm_read(ipu, IPU_SRM_PRI2); -- val &= ~DP_S_SRM_MODE_MASK; -- val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -- DP_S_SRM_MODE_NOW; -- ipu_cm_write(ipu, val, IPU_SRM_PRI2); --} --EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -- --enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_ARGB1555: -- case DRM_FORMAT_ABGR1555: -- case DRM_FORMAT_RGBA5551: -- case DRM_FORMAT_BGRA5551: -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_ARGB4444: -- case DRM_FORMAT_XRGB8888: -- case DRM_FORMAT_XBGR8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- return IPUV3_COLORSPACE_RGB; -- case DRM_FORMAT_YUYV: -- case DRM_FORMAT_UYVY: -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- case DRM_FORMAT_NV12: -- case DRM_FORMAT_NV21: -- case DRM_FORMAT_NV16: -- case DRM_FORMAT_NV61: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -- --enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return IPUV3_COLORSPACE_YUV; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- return IPUV3_COLORSPACE_RGB; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -- --bool ipu_pixelformat_is_planar(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return true; -- } -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -- --enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) --{ -- switch (mbus_code & 0xf000) { -- case 0x1000: -- return IPUV3_COLORSPACE_RGB; -- case 0x2000: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -- --int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- /* -- * for the planar YUV formats, the stride passed to -- * cpmem must be the stride in bytes of the Y plane. -- * And all the planar YUV formats have an 8-bit -- * Y component. -- */ -- return (8 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_UYVY: -- return (16 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- return (24 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_XRGB32: -- return (32 * pixel_stride) >> 3; -- default: -- break; -- } -- -- return -EINVAL; --} --EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -- --int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- switch (degrees) { -- case 0: -- vf = hf = r90 = 0; -- break; -- case 90: -- vf = hf = 0; -- r90 = 1; -- break; -- case 180: -- vf = hf = 1; -- r90 = 0; -- break; -- case 270: -- vf = hf = r90 = 1; -- break; -- default: -- return -EINVAL; -- } -- -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -- --int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- r90 = ((u32)mode >> 2) & 0x1; -- hf = ((u32)mode >> 1) & 0x1; -- vf = ((u32)mode >> 0) & 0x1; -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -- case IPU_ROTATE_NONE: -- *degrees = 0; -- break; -- case IPU_ROTATE_90_RIGHT: -- *degrees = 90; -- break; -- case IPU_ROTATE_180: -- *degrees = 180; -- break; -- case IPU_ROTATE_90_LEFT: -- *degrees = 270; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -- --struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) --{ -- struct ipuv3_channel *channel; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, num); -- -- if (num > 63) -- return ERR_PTR(-ENODEV); -- -- mutex_lock(&ipu->channel_lock); -- -- list_for_each_entry(channel, &ipu->channels, list) { -- if (channel->num == num) { -- channel = ERR_PTR(-EBUSY); -- goto out; -- } -- } -- -- channel = kzalloc(sizeof(*channel), GFP_KERNEL); -- if (!channel) { -- channel = ERR_PTR(-ENOMEM); -- goto out; -- } -- -- channel->num = num; -- channel->ipu = ipu; -- list_add(&channel->list, &ipu->channels); -- --out: -- mutex_unlock(&ipu->channel_lock); -- -- return channel; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get); -- --void ipu_idmac_put(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -- -- mutex_lock(&ipu->channel_lock); -- -- list_del(&channel->list); -- kfree(channel); -- -- mutex_unlock(&ipu->channel_lock); --} --EXPORT_SYMBOL_GPL(ipu_idmac_put); -- --#define idma_mask(ch) (1 << ((ch) & 0x1f)) -- --/* -- * This is an undocumented feature, a write one to a channel bit in -- * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -- * internal current buffer pointer so that transfers start from buffer -- * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -- * only says these are read-only registers). This operation is required -- * for channel linking to work correctly, for instance video capture -- * pipelines that carry out image rotations will fail after the first -- * streaming unless this function is called for each channel before -- * re-enabling the channels. -- */ --static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); --} -- --void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -- bool doublebuffer) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- if (doublebuffer) -- reg |= idma_mask(channel->num); -- else -- reg &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -- --static const struct { -- int chnum; -- u32 reg; -- int shift; --} idmac_lock_en_info[] = { -- { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -- { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -- { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -- { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -- { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -- { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -- { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -- { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -- { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -- { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -- { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -- { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -- { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -- { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -- { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -- { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -- { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, --}; -- --int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 bursts, regval; -- int i; -- -- switch (num_bursts) { -- case 0: -- case 1: -- bursts = 0x00; /* locking disabled */ -- break; -- case 2: -- bursts = 0x01; -- break; -- case 4: -- bursts = 0x02; -- break; -- case 8: -- bursts = 0x03; -- break; -- default: -- return -EINVAL; -- } -- -- /* -- * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -- * i.MX53 channel arbitration locking doesn't seem to work properly. -- * Allow enabling the lock feature on IPUv3H / i.MX6 only. -- */ -- if (bursts && ipu->ipu_type != IPUV3H) -- return -EINVAL; -- -- for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -- if (channel->num == idmac_lock_en_info[i].chnum) -- break; -- } -- if (i >= ARRAY_SIZE(idmac_lock_en_info)) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -- regval &= ~(0x03 << idmac_lock_en_info[i].shift); -- regval |= (bursts << idmac_lock_en_info[i].shift); -- ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val |= IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val |= IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val |= mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_enable); -- --int ipu_module_disable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val &= ~IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val &= ~IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_disable); -- --int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -- --bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg = 0; -- -- spin_lock_irqsave(&ipu->lock, flags); -- switch (buf_num) { -- case 0: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -- break; -- case 1: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -- break; -- case 2: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -- break; -- } -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return ((reg & idma_mask(channel->num)) != 0); --} --EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -- --void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Mark buffer as ready. */ -- if (buf_num == 0) -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- else -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -- --void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -- switch (buf_num) { -- case 0: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- break; -- case 1: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- break; -- case 2: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -- break; -- default: -- break; -- } -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -- --int ipu_idmac_enable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val |= idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) --{ -- return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -- --int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long timeout; -- -- timeout = jiffies + msecs_to_jiffies(ms); -- while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -- idma_mask(channel->num)) { -- if (time_after(jiffies, timeout)) -- return -ETIMEDOUT; -- cpu_relax(); -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -- --int ipu_idmac_disable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Disable DMA channel(s) */ -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- /* Set channel buffers NOT to be ready */ -- ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF0_RDY(channel->num)); -- } -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF1_RDY(channel->num)); -- } -- -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- /* Reset the double buffer */ -- val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -- --/* -- * The imx6 rev. D TRM says that enabling the WM feature will increase -- * a channel's priority. Refer to Table 36-8 Calculated priority value. -- * The sub-module that is the sink or source for the channel must enable -- * watermark signal for this to take effect (SMFC_WM for instance). -- */ --void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -- if (enable) -- val |= 1 << (channel->num % 32); -- else -- val &= ~(1 << (channel->num % 32)); -- ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -- --static int ipu_memory_reset(struct ipu_soc *ipu) --{ -- unsigned long timeout; -- -- ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -- -- timeout = jiffies + msecs_to_jiffies(1000); -- while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -- if (time_after(jiffies, timeout)) -- return -ETIME; -- cpu_relax(); -- } -- -- return 0; --} -- --/* -- * Set the source mux for the given CSI. Selects either parallel or -- * MIPI CSI2 sources. -- */ --void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) --{ -- unsigned long flags; -- u32 val, mask; -- -- mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -- IPU_CONF_CSI0_DATA_SOURCE; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (mipi_csi2) -- val |= mask; -- else -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -- --/* -- * Set the source mux for the IC. Selects either CSI[01] or the VDI. -- */ --void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) --{ -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (vdi) -- val |= IPU_CONF_IC_INPUT; -- else -- val &= ~IPU_CONF_IC_INPUT; -- -- if (csi_id == 1) -- val |= IPU_CONF_CSI_SEL; -- else -- val &= ~IPU_CONF_CSI_SEL; -- -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -- -- --/* Frame Synchronization Unit Channel Linking */ -- --struct fsu_link_reg_info { -- int chno; -- u32 reg; -- u32 mask; -- u32 val; --}; -- --struct fsu_link_info { -- struct fsu_link_reg_info src; -- struct fsu_link_reg_info sink; --}; -- --static const struct fsu_link_info fsu_link_info[] = { -- { -- .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -- FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -- FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -- FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -- FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -- FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -- FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -- }, { -- .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -- .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -- FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -- }, --}; -- --static const struct fsu_link_info *find_fsu_link_info(int src, int sink) --{ -- int i; -- -- for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -- if (src == fsu_link_info[i].src.chno && -- sink == fsu_link_info[i].sink.chno) -- return &fsu_link_info[i]; -- } -- -- return NULL; --} -- --/* -- * Links a source channel to a sink channel in the FSU. -- */ --int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- src_reg |= link->src.val; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- sink_reg |= link->sink.val; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_link); -- --/* -- * Unlinks source and sink channels in the FSU. -- */ --int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -- --/* Link IDMAC channels in the FSU */ --int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_link(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_link); -- --/* Unlink IDMAC channels in the FSU */ --int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_unlink(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -- --struct ipu_devtype { -- const char *name; -- unsigned long cm_ofs; -- unsigned long cpmem_ofs; -- unsigned long srm_ofs; -- unsigned long tpm_ofs; -- unsigned long csi0_ofs; -- unsigned long csi1_ofs; -- unsigned long ic_ofs; -- unsigned long disp0_ofs; -- unsigned long disp1_ofs; -- unsigned long dc_tmpl_ofs; -- unsigned long vdi_ofs; -- enum ipuv3_type type; --}; -- --static struct ipu_devtype ipu_type_imx51 = { -- .name = "IPUv3EX", -- .cm_ofs = 0x1e000000, -- .cpmem_ofs = 0x1f000000, -- .srm_ofs = 0x1f040000, -- .tpm_ofs = 0x1f060000, -- .csi0_ofs = 0x1e030000, -- .csi1_ofs = 0x1e038000, -- .ic_ofs = 0x1e020000, -- .disp0_ofs = 0x1e040000, -- .disp1_ofs = 0x1e048000, -- .dc_tmpl_ofs = 0x1f080000, -- .vdi_ofs = 0x1e068000, -- .type = IPUV3EX, --}; -- --static struct ipu_devtype ipu_type_imx53 = { -- .name = "IPUv3M", -- .cm_ofs = 0x06000000, -- .cpmem_ofs = 0x07000000, -- .srm_ofs = 0x07040000, -- .tpm_ofs = 0x07060000, -- .csi0_ofs = 0x06030000, -- .csi1_ofs = 0x06038000, -- .ic_ofs = 0x06020000, -- .disp0_ofs = 0x06040000, -- .disp1_ofs = 0x06048000, -- .dc_tmpl_ofs = 0x07080000, -- .vdi_ofs = 0x06068000, -- .type = IPUV3M, --}; -- --static struct ipu_devtype ipu_type_imx6q = { -- .name = "IPUv3H", -- .cm_ofs = 0x00200000, -- .cpmem_ofs = 0x00300000, -- .srm_ofs = 0x00340000, -- .tpm_ofs = 0x00360000, -- .csi0_ofs = 0x00230000, -- .csi1_ofs = 0x00238000, -- .ic_ofs = 0x00220000, -- .disp0_ofs = 0x00240000, -- .disp1_ofs = 0x00248000, -- .dc_tmpl_ofs = 0x00380000, -- .vdi_ofs = 0x00268000, -- .type = IPUV3H, --}; -- --static const struct of_device_id imx_ipu_dt_ids[] = { -- { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -- { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -- { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -- { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -- { /* sentinel */ } --}; --MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -- --static int ipu_submodules_init(struct ipu_soc *ipu, -- struct platform_device *pdev, unsigned long ipu_base, -- struct clk *ipu_clk) --{ -- char *unit; -- int ret; -- struct device *dev = &pdev->dev; -- const struct ipu_devtype *devtype = ipu->devtype; -- -- ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -- if (ret) { -- unit = "cpmem"; -- goto err_cpmem; -- } -- -- ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -- IPU_CONF_CSI0_EN, ipu_clk); -- if (ret) { -- unit = "csi0"; -- goto err_csi_0; -- } -- -- ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -- IPU_CONF_CSI1_EN, ipu_clk); -- if (ret) { -- unit = "csi1"; -- goto err_csi_1; -- } -- -- ret = ipu_ic_init(ipu, dev, -- ipu_base + devtype->ic_ofs, -- ipu_base + devtype->tpm_ofs); -- if (ret) { -- unit = "ic"; -- goto err_ic; -- } -- -- ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -- IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -- IPU_CONF_IC_INPUT); -- if (ret) { -- unit = "vdi"; -- goto err_vdi; -- } -- -- ret = ipu_image_convert_init(ipu, dev); -- if (ret) { -- unit = "image_convert"; -- goto err_image_convert; -- } -- -- ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -- IPU_CONF_DI0_EN, ipu_clk); -- if (ret) { -- unit = "di0"; -- goto err_di_0; -- } -- -- ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -- IPU_CONF_DI1_EN, ipu_clk); -- if (ret) { -- unit = "di1"; -- goto err_di_1; -- } -- -- ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -- IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -- if (ret) { -- unit = "dc_template"; -- goto err_dc; -- } -- -- ret = ipu_dmfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -- if (ret) { -- unit = "dmfc"; -- goto err_dmfc; -- } -- -- ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -- if (ret) { -- unit = "dp"; -- goto err_dp; -- } -- -- ret = ipu_smfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -- if (ret) { -- unit = "smfc"; -- goto err_smfc; -- } -- -- return 0; -- --err_smfc: -- ipu_dp_exit(ipu); --err_dp: -- ipu_dmfc_exit(ipu); --err_dmfc: -- ipu_dc_exit(ipu); --err_dc: -- ipu_di_exit(ipu, 1); --err_di_1: -- ipu_di_exit(ipu, 0); --err_di_0: -- ipu_image_convert_exit(ipu); --err_image_convert: -- ipu_vdi_exit(ipu); --err_vdi: -- ipu_ic_exit(ipu); --err_ic: -- ipu_csi_exit(ipu, 1); --err_csi_1: -- ipu_csi_exit(ipu, 0); --err_csi_0: -- ipu_cpmem_exit(ipu); --err_cpmem: -- dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -- return ret; --} -- --static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) --{ -- unsigned long status; -- int i, bit, irq; -- -- for (i = 0; i < num_regs; i++) { -- -- status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -- status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -- -- for_each_set_bit(bit, &status, 32) { -- irq = irq_linear_revmap(ipu->domain, -- regs[i] * 32 + bit); -- if (irq) -- generic_handle_irq(irq); -- } -- } --} -- --static void ipu_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --static void ipu_err_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 4, 5, 8, 9}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --int ipu_map_irq(struct ipu_soc *ipu, int irq) --{ -- int virq; -- -- virq = irq_linear_revmap(ipu->domain, irq); -- if (!virq) -- virq = irq_create_mapping(ipu->domain, irq); -- -- return virq; --} --EXPORT_SYMBOL_GPL(ipu_map_irq); -- --int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -- enum ipu_channel_irq irq_type) --{ -- return ipu_map_irq(ipu, irq_type + channel->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -- --static void ipu_submodules_exit(struct ipu_soc *ipu) --{ -- ipu_smfc_exit(ipu); -- ipu_dp_exit(ipu); -- ipu_dmfc_exit(ipu); -- ipu_dc_exit(ipu); -- ipu_di_exit(ipu, 1); -- ipu_di_exit(ipu, 0); -- ipu_image_convert_exit(ipu); -- ipu_vdi_exit(ipu); -- ipu_ic_exit(ipu); -- ipu_csi_exit(ipu, 1); -- ipu_csi_exit(ipu, 0); -- ipu_cpmem_exit(ipu); --} -- --static int platform_remove_devices_fn(struct device *dev, void *unused) --{ -- struct platform_device *pdev = to_platform_device(dev); -- -- platform_device_unregister(pdev); -- -- return 0; --} -- --static void platform_device_unregister_children(struct platform_device *pdev) --{ -- device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); --} -- --struct ipu_platform_reg { -- struct ipu_client_platformdata pdata; -- const char *name; --}; -- --/* These must be in the order of the corresponding device tree port nodes */ --static struct ipu_platform_reg client_reg[] = { -- { -- .pdata = { -- .csi = 0, -- .dma[0] = IPUV3_CHANNEL_CSI0, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .csi = 1, -- .dma[0] = IPUV3_CHANNEL_CSI1, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .di = 0, -- .dc = 5, -- .dp = IPU_DP_FLOW_SYNC_BG, -- .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -- .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -- }, -- .name = "imx-ipuv3-crtc", -- }, { -- .pdata = { -- .di = 1, -- .dc = 1, -- .dp = -EINVAL, -- .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-crtc", -- }, --}; -- --static DEFINE_MUTEX(ipu_client_id_mutex); --static int ipu_client_id; -- --static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) --{ -- struct device *dev = ipu->dev; -- unsigned i; -- int id, ret; -- -- mutex_lock(&ipu_client_id_mutex); -- id = ipu_client_id; -- ipu_client_id += ARRAY_SIZE(client_reg); -- mutex_unlock(&ipu_client_id_mutex); -- -- for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -- struct ipu_platform_reg *reg = &client_reg[i]; -- struct platform_device *pdev; -- struct device_node *of_node; -- -- /* Associate subdevice with the corresponding port node */ -- of_node = of_graph_get_port_by_id(dev->of_node, i); -- if (!of_node) { -- dev_info(dev, -- "no port@%d node in %pOF, not using %s%d\n", -- i, dev->of_node, -- (i / 2) ? "DI" : "CSI", i % 2); -- continue; -- } -- -- pdev = platform_device_alloc(reg->name, id++); -- if (!pdev) { -- ret = -ENOMEM; -- goto err_register; -- } -- -- pdev->dev.parent = dev; -- -- reg->pdata.of_node = of_node; -- ret = platform_device_add_data(pdev, ®->pdata, -- sizeof(reg->pdata)); -- if (!ret) -- ret = platform_device_add(pdev); -- if (ret) { -- platform_device_put(pdev); -- goto err_register; -- } -- } -- -- return 0; -- --err_register: -- platform_device_unregister_children(to_platform_device(dev)); -- -- return ret; --} -- -- --static int ipu_irq_init(struct ipu_soc *ipu) --{ -- struct irq_chip_generic *gc; -- struct irq_chip_type *ct; -- unsigned long unused[IPU_NUM_IRQS / 32] = { -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x4077ffff, 0xffe7e1fd, -- 0x23fffffe, 0x8880fff0, -- 0xf98fe7d0, 0xfff81fff, -- 0x400100d0, 0xffe000fd, -- 0x00000000, -- }; -- int ret, i; -- -- ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -- &irq_generic_chip_ops, ipu); -- if (!ipu->domain) { -- dev_err(ipu->dev, "failed to add irq domain\n"); -- return -ENODEV; -- } -- -- ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -- handle_level_irq, 0, 0, 0); -- if (ret < 0) { -- dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -- irq_domain_remove(ipu->domain); -- return ret; -- } -- -- /* Mask and clear all interrupts */ -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -- ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -- } -- -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- gc = irq_get_domain_generic_chip(ipu->domain, i); -- gc->reg_base = ipu->cm_reg; -- gc->unused = unused[i / 32]; -- ct = gc->chip_types; -- ct->chip.irq_ack = irq_gc_ack_set_bit; -- ct->chip.irq_mask = irq_gc_mask_clr_bit; -- ct->chip.irq_unmask = irq_gc_mask_set_bit; -- ct->regs.ack = IPU_INT_STAT(i / 32); -- ct->regs.mask = IPU_INT_CTRL(i / 32); -- } -- -- irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -- irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -- ipu); -- -- return 0; --} -- --static void ipu_irq_exit(struct ipu_soc *ipu) --{ -- int i, irq; -- -- irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -- irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -- -- /* TODO: remove irq_domain_generic_chips */ -- -- for (i = 0; i < IPU_NUM_IRQS; i++) { -- irq = irq_linear_revmap(ipu->domain, i); -- if (irq) -- irq_dispose_mapping(irq); -- } -- -- irq_domain_remove(ipu->domain); --} -- --void ipu_dump(struct ipu_soc *ipu) --{ -- int i; -- -- dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -- dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -- for (i = 0; i < 15; i++) -- dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -- ipu_cm_read(ipu, IPU_INT_CTRL(i))); --} --EXPORT_SYMBOL_GPL(ipu_dump); -- --static int ipu_probe(struct platform_device *pdev) --{ -- struct device_node *np = pdev->dev.of_node; -- struct ipu_soc *ipu; -- struct resource *res; -- unsigned long ipu_base; -- int ret, irq_sync, irq_err; -- const struct ipu_devtype *devtype; -- -- devtype = of_device_get_match_data(&pdev->dev); -- if (!devtype) -- return -EINVAL; -- -- irq_sync = platform_get_irq(pdev, 0); -- irq_err = platform_get_irq(pdev, 1); -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- -- dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -- irq_sync, irq_err); -- -- if (!res || irq_sync < 0 || irq_err < 0) -- return -ENODEV; -- -- ipu_base = res->start; -- -- ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -- if (!ipu) -- return -ENODEV; -- -- ipu->id = of_alias_get_id(np, "ipu"); -- if (ipu->id < 0) -- ipu->id = 0; -- -- if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -- IS_ENABLED(CONFIG_DRM)) { -- ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -- "fsl,prg", ipu->id); -- if (!ipu->prg_priv) -- return -EPROBE_DEFER; -- } -- -- ipu->devtype = devtype; -- ipu->ipu_type = devtype->type; -- -- spin_lock_init(&ipu->lock); -- mutex_init(&ipu->channel_lock); -- INIT_LIST_HEAD(&ipu->channels); -- -- dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -- ipu_base + devtype->cm_ofs); -- dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -- dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -- ipu_base + devtype->cpmem_ofs); -- dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -- ipu_base + devtype->csi0_ofs); -- dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -- ipu_base + devtype->csi1_ofs); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->ic_ofs); -- dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -- ipu_base + devtype->disp0_ofs); -- dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -- ipu_base + devtype->disp1_ofs); -- dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -- ipu_base + devtype->srm_ofs); -- dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -- ipu_base + devtype->tpm_ofs); -- dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -- dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -- dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -- ipu_base + devtype->vdi_ofs); -- -- ipu->cm_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs, PAGE_SIZE); -- ipu->idmac_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -- PAGE_SIZE); -- -- if (!ipu->cm_reg || !ipu->idmac_reg) -- return -ENOMEM; -- -- ipu->clk = devm_clk_get(&pdev->dev, "bus"); -- if (IS_ERR(ipu->clk)) { -- ret = PTR_ERR(ipu->clk); -- dev_err(&pdev->dev, "clk_get failed with %d", ret); -- return ret; -- } -- -- platform_set_drvdata(pdev, ipu); -- -- ret = clk_prepare_enable(ipu->clk); -- if (ret) { -- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -- return ret; -- } -- -- ipu->dev = &pdev->dev; -- ipu->irq_sync = irq_sync; -- ipu->irq_err = irq_err; -- -- ret = device_reset(&pdev->dev); -- if (ret) { -- dev_err(&pdev->dev, "failed to reset: %d\n", ret); -- goto out_failed_reset; -- } -- ret = ipu_memory_reset(ipu); -- if (ret) -- goto out_failed_reset; -- -- ret = ipu_irq_init(ipu); -- if (ret) -- goto out_failed_irq; -- -- /* Set MCU_T to divide MCU access window into 2 */ -- ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -- IPU_DISP_GEN); -- -- ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -- if (ret) -- goto failed_submodules_init; -- -- ret = ipu_add_client_devices(ipu, ipu_base); -- if (ret) { -- dev_err(&pdev->dev, "adding client devices failed with %d\n", -- ret); -- goto failed_add_clients; -- } -- -- dev_info(&pdev->dev, "%s probed\n", devtype->name); -- -- return 0; -- --failed_add_clients: -- ipu_submodules_exit(ipu); --failed_submodules_init: -- ipu_irq_exit(ipu); --out_failed_irq: --out_failed_reset: -- clk_disable_unprepare(ipu->clk); -- return ret; --} -- --static int ipu_remove(struct platform_device *pdev) --{ -- struct ipu_soc *ipu = platform_get_drvdata(pdev); -- -- platform_device_unregister_children(pdev); -- ipu_submodules_exit(ipu); -- ipu_irq_exit(ipu); -- -- clk_disable_unprepare(ipu->clk); -- -- return 0; --} -- --static struct platform_driver imx_ipu_driver = { -- .driver = { -- .name = "imx-ipuv3", -- .of_match_table = imx_ipu_dt_ids, -- }, -- .probe = ipu_probe, -- .remove = ipu_remove, --}; -- --static struct platform_driver * const drivers[] = { --#if IS_ENABLED(CONFIG_DRM) -- &ipu_pre_drv, -- &ipu_prg_drv, --#endif -- &imx_ipu_driver, --}; -- --static int __init imx_ipu_init(void) --{ -- return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_init(imx_ipu_init); -- --static void __exit imx_ipu_exit(void) --{ -- platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_exit(imx_ipu_exit); -- --MODULE_ALIAS("platform:imx-ipuv3"); --MODULE_DESCRIPTION("i.MX IPU v3 driver"); --MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); --MODULE_LICENSE("GPL"); ---- a/drivers/gpu/ipu-v3/ipu-cpmem.c -+++ /dev/null -@@ -1,976 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#include <linux/types.h> --#include <linux/bitrev.h> --#include <linux/io.h> --#include <linux/sizes.h> --#include <drm/drm_fourcc.h> --#include "ipu-prv.h" -- --struct ipu_cpmem_word { -- u32 data[5]; -- u32 res[3]; --}; -- --struct ipu_ch_param { -- struct ipu_cpmem_word word[2]; --}; -- --struct ipu_cpmem { -- struct ipu_ch_param __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- --#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -- --#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) --#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) --#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) --#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) --#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) --#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -- --#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) --#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) --#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) --#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) --#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) --#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) --#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) --#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) --#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) --#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) --#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) --#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) --#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) --#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) --#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) --#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) --#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) --#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) --#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) --#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) --#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) --#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) --#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) --#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) --#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) --#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) --#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) --#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) --#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) --#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) --#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) --#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) --#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) --#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) --#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) --#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) --#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) --#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) --#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) --#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) --#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) --#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) --#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) --#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) --#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) --#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) --#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) --#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) --#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) --#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -- --static inline struct ipu_ch_param __iomem * --ipu_get_cpmem(struct ipuv3_channel *ch) --{ -- struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -- -- return cpmem->base + ch->num; --} -- --static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = readl(&base->word[word].data[i]); -- val &= ~(mask << ofs); -- val |= v << ofs; -- writel(val, &base->word[word].data[i]); -- -- if ((bit + size - 1) / 32 > i) { -- val = readl(&base->word[word].data[i + 1]); -- val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -- val |= v >> (ofs ? (32 - ofs) : 0); -- writel(val, &base->word[word].data[i + 1]); -- } --} -- --static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val = 0; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = (readl(&base->word[word].data[i]) >> ofs) & mask; -- -- if ((bit + size - 1) / 32 > i) { -- u32 tmp; -- -- tmp = readl(&base->word[word].data[i + 1]); -- tmp &= mask >> (ofs ? (32 - ofs) : 0); -- val |= tmp << (ofs ? (32 - ofs) : 0); -- } -- -- return val; --} -- --/* -- * The V4L2 spec defines packed RGB formats in memory byte order, which from -- * point of view of the IPU corresponds to little-endian words with the first -- * component in the least significant bits. -- * The DRM pixel formats and IPU internal representation are ordered the other -- * way around, with the first named component ordered at the most significant -- * bits. Further, V4L2 formats are not well defined: -- * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -- * We choose the interpretation which matches GStreamer behavior. -- */ --static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_RGB565: -- /* -- * Here we choose the 'corrected' interpretation of RGBP, a -- * little-endian 16-bit word with the red component at the most -- * significant bits: -- * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -- */ -- return DRM_FORMAT_RGB565; -- case V4L2_PIX_FMT_BGR24: -- /* B G R <=> [24:0] R:G:B */ -- return DRM_FORMAT_RGB888; -- case V4L2_PIX_FMT_RGB24: -- /* R G B <=> [24:0] B:G:R */ -- return DRM_FORMAT_BGR888; -- case V4L2_PIX_FMT_BGR32: -- /* B G R A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_RGB32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ABGR32: -- /* B G R A <=> [32:0] A:R:G:B */ -- return DRM_FORMAT_ARGB8888; -- case V4L2_PIX_FMT_XBGR32: -- /* B G R X <=> [32:0] X:R:G:B */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_BGRA32: -- /* A B G R <=> [32:0] R:G:B:A */ -- return DRM_FORMAT_RGBA8888; -- case V4L2_PIX_FMT_BGRX32: -- /* X B G R <=> [32:0] R:G:B:X */ -- return DRM_FORMAT_RGBX8888; -- case V4L2_PIX_FMT_RGBA32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_ABGR8888; -- case V4L2_PIX_FMT_RGBX32: -- /* R G B X <=> [32:0] X:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ARGB32: -- /* A R G B <=> [32:0] B:G:R:A */ -- return DRM_FORMAT_BGRA8888; -- case V4L2_PIX_FMT_XRGB32: -- /* X R G B <=> [32:0] B:G:R:X */ -- return DRM_FORMAT_BGRX8888; -- case V4L2_PIX_FMT_UYVY: -- return DRM_FORMAT_UYVY; -- case V4L2_PIX_FMT_YUYV: -- return DRM_FORMAT_YUYV; -- case V4L2_PIX_FMT_YUV420: -- return DRM_FORMAT_YUV420; -- case V4L2_PIX_FMT_YUV422P: -- return DRM_FORMAT_YUV422; -- case V4L2_PIX_FMT_YVU420: -- return DRM_FORMAT_YVU420; -- case V4L2_PIX_FMT_NV12: -- return DRM_FORMAT_NV12; -- case V4L2_PIX_FMT_NV16: -- return DRM_FORMAT_NV16; -- } -- -- return -EINVAL; --} -- --void ipu_cpmem_zero(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- void __iomem *base = p; -- int i; -- -- for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -- writel(0, base + i * sizeof(u32)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -- --void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -- --void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -- --void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -- --void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- u32 val; -- -- if (ipu->ipu_type == IPUV3EX) -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -- val |= 1 << (ch->num % 32); -- ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -- --void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) --{ -- WARN_ON_ONCE(buf & 0x7); -- -- if (bufnum) -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -- else -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -- --void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) --{ -- WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -- --void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -- u32 pixelformat) --{ -- u32 ilo, sly, sluv; -- -- if (stride < 0) { -- stride = -stride; -- ilo = 0x100000 - (stride / 8); -- } else { -- ilo = stride / 8; -- } -- -- sly = (stride * 2) - 1; -- -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- sluv = stride / 2 - 1; -- break; -- case V4L2_PIX_FMT_NV12: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_YUV422P: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_NV16: -- sluv = stride * 2 - 1; -- break; -- default: -- sluv = 0; -- break; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -- if (sluv) -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -- --void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) --{ -- id &= 0x3; -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -- --int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) --{ -- return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -- --void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -- --void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -- --void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -- enum ipu_rotate_mode rot) --{ -- u32 temp_rot = bitrev8(rot) >> 5; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -- --int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -- const struct ipu_rgb *rgb) --{ -- int bpp = 0, npb = 0, ro, go, bo, to; -- -- ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -- go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -- bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -- to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -- -- if (rgb->transp.length) { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -- rgb->transp.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -- } else { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -- rgb->bits_per_pixel); -- } -- -- switch (rgb->bits_per_pixel) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -- --int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) --{ -- int bpp = 0, npb = 0; -- -- switch (width) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -- --void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) --{ -- switch (pixel_format) { -- case V4L2_PIX_FMT_UYVY: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- case V4L2_PIX_FMT_YUYV: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- } --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -- --void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -- unsigned int uv_stride, -- unsigned int u_offset, unsigned int v_offset) --{ -- WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -- --static const struct ipu_rgb def_xrgb_32 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_xbgr_32 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgbx_32 = { -- .red = { .offset = 24, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 8, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_bgrx_32 = { -- .red = { .offset = 8, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 24, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgb_24 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_bgr_24 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_rgb_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16 = { -- .red = { .offset = 10, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16_4444 = { -- .red = { .offset = 8, .length = 4, }, -- .green = { .offset = 4, .length = 4, }, -- .blue = { .offset = 0, .length = 4, }, -- .transp = { .offset = 12, .length = 4, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_abgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 10, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_rgba_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 1, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgra_16 = { -- .red = { .offset = 1, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) --#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 4) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 2) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2)) + (x)) --#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * y) + (x)) -- --#define NUM_ALPHA_CHANNELS 7 -- --/* See Table 37-12. Alpha channels mapping. */ --static int ipu_channel_albm(int ch_num) --{ -- switch (ch_num) { -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -- case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -- case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -- case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -- case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -- case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -- case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -- default: -- return -EINVAL; -- } --} -- --static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- int albm; -- u32 val; -- -- albm = ipu_channel_albm(ch->num); -- if (albm < 0) -- return; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -- ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -- val |= BIT(ch->num); -- ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); --} -- --int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV12: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV16: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_UYVY: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUYV: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_XBGR8888: -- ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -- break; -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_XRGB8888: -- ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -- break; -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_RGBX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -- break; -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -- break; -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_BGR888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -- break; -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_RGB888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -- break; -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_RGB565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -- break; -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_BGR565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -- break; -- case DRM_FORMAT_ARGB1555: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16); -- break; -- case DRM_FORMAT_ABGR1555: -- ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -- break; -- case DRM_FORMAT_RGBA5551: -- ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -- break; -- case DRM_FORMAT_BGRA5551: -- ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -- break; -- case DRM_FORMAT_ARGB4444: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -- break; -- default: -- return -EINVAL; -- } -- -- switch (drm_fourcc) { -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_cpmem_set_separate_alpha(ch); -- break; -- default: -- break; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -- --int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) --{ -- struct v4l2_pix_format *pix = &image->pix; -- int offset, u_offset, v_offset; -- int ret = 0; -- -- pr_debug("%s: resolution: %dx%d stride: %d\n", -- __func__, pix->width, pix->height, -- pix->bytesperline); -- -- ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -- ipu_cpmem_set_stride(ch, pix->bytesperline); -- -- ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -- -- switch (pix->pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YVU420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YUV422P: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV12: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV16: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_RGB565: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- offset = image->rect.left * 4 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_BGR24: -- offset = image->rect.left * 3 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR8: -- case V4L2_PIX_FMT_SGBRG8: -- case V4L2_PIX_FMT_SGRBG8: -- case V4L2_PIX_FMT_SRGGB8: -- case V4L2_PIX_FMT_GREY: -- offset = image->rect.left + image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR16: -- case V4L2_PIX_FMT_SGBRG16: -- case V4L2_PIX_FMT_SGRBG16: -- case V4L2_PIX_FMT_SRGGB16: -- case V4L2_PIX_FMT_Y16: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- default: -- /* This should not happen */ -- WARN_ON(1); -- offset = 0; -- ret = -EINVAL; -- } -- -- ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -- ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -- --void ipu_cpmem_dump(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- struct ipu_soc *ipu = ch->ipu; -- int chno = ch->num; -- -- dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[0].data[0]), -- readl(&p->word[0].data[1]), -- readl(&p->word[0].data[2]), -- readl(&p->word[0].data[3]), -- readl(&p->word[0].data[4])); -- dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[1].data[0]), -- readl(&p->word[1].data[1]), -- readl(&p->word[1].data[2]), -- readl(&p->word[1].data[3]), -- readl(&p->word[1].data[4])); -- dev_dbg(ipu->dev, "PFS 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -- dev_dbg(ipu->dev, "BPP 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -- dev_dbg(ipu->dev, "NPB 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -- -- dev_dbg(ipu->dev, "FW %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -- dev_dbg(ipu->dev, "FH %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -- dev_dbg(ipu->dev, "EBA0 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -- dev_dbg(ipu->dev, "EBA1 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -- dev_dbg(ipu->dev, "Stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -- dev_dbg(ipu->dev, "scan_order %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -- dev_dbg(ipu->dev, "uv_stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -- dev_dbg(ipu->dev, "u_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -- dev_dbg(ipu->dev, "v_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -- -- dev_dbg(ipu->dev, "Width0 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -- dev_dbg(ipu->dev, "Width1 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -- dev_dbg(ipu->dev, "Width2 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -- dev_dbg(ipu->dev, "Width3 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -- dev_dbg(ipu->dev, "Offset0 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -- dev_dbg(ipu->dev, "Offset1 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -- dev_dbg(ipu->dev, "Offset2 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -- dev_dbg(ipu->dev, "Offset3 %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_cpmem *cpmem; -- -- cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -- if (!cpmem) -- return -ENOMEM; -- -- ipu->cpmem_priv = cpmem; -- -- spin_lock_init(&cpmem->lock); -- cpmem->base = devm_ioremap(dev, base, SZ_128K); -- if (!cpmem->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -- base, cpmem->base); -- cpmem->ipu = ipu; -- -- return 0; --} -- --void ipu_cpmem_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-csi.c -+++ /dev/null -@@ -1,821 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> --#include <linux/videodev2.h> --#include <uapi/linux/v4l2-mediabus.h> --#include <linux/clk.h> --#include <linux/clk-provider.h> --#include <linux/clkdev.h> -- --#include "ipu-prv.h" -- --struct ipu_csi { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_ipu; /* IPU bus clock */ -- spinlock_t lock; -- bool inuse; -- struct ipu_soc *ipu; --}; -- --/* CSI Register Offsets */ --#define CSI_SENS_CONF 0x0000 --#define CSI_SENS_FRM_SIZE 0x0004 --#define CSI_ACT_FRM_SIZE 0x0008 --#define CSI_OUT_FRM_CTRL 0x000c --#define CSI_TST_CTRL 0x0010 --#define CSI_CCIR_CODE_1 0x0014 --#define CSI_CCIR_CODE_2 0x0018 --#define CSI_CCIR_CODE_3 0x001c --#define CSI_MIPI_DI 0x0020 --#define CSI_SKIP 0x0024 --#define CSI_CPD_CTRL 0x0028 --#define CSI_CPD_RC(n) (0x002c + ((n)*4)) --#define CSI_CPD_RS(n) (0x004c + ((n)*4)) --#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) --#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) --#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) --#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) --#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) --#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) --#define CSI_CPD_OFFSET1 0x00ec --#define CSI_CPD_OFFSET2 0x00f0 -- --/* CSI Register Fields */ --#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 --#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 --#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L --#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L --#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L --#define CSI_SENS_CONF_DATA_FMT_BAYER 3L --#define CSI_SENS_CONF_DATA_FMT_RGB565 4L --#define CSI_SENS_CONF_DATA_FMT_RGB555 5L --#define CSI_SENS_CONF_DATA_FMT_RGB444 6L --#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -- --#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 --#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 --#define CSI_SENS_CONF_DATA_POL_SHIFT 2 --#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 --#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 --#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 --#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 --#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 --#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 --#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -- --#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 --#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 --#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 --#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 --#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 --#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 --#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -- --#define CSI_DATA_DEST_IC 2 --#define CSI_DATA_DEST_IDMAC 4 -- --#define CSI_CCIR_ERR_DET_EN 0x01000000 --#define CSI_HORI_DOWNSIZE_EN 0x80000000 --#define CSI_VERT_DOWNSIZE_EN 0x40000000 --#define CSI_TEST_GEN_MODE_EN 0x01000000 -- --#define CSI_HSC_MASK 0x1fff0000 --#define CSI_HSC_SHIFT 16 --#define CSI_VSC_MASK 0x00000fff --#define CSI_VSC_SHIFT 0 -- --#define CSI_TEST_GEN_R_MASK 0x000000ff --#define CSI_TEST_GEN_R_SHIFT 0 --#define CSI_TEST_GEN_G_MASK 0x0000ff00 --#define CSI_TEST_GEN_G_SHIFT 8 --#define CSI_TEST_GEN_B_MASK 0x00ff0000 --#define CSI_TEST_GEN_B_SHIFT 16 -- --#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 --#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 --#define CSI_SKIP_SMFC_MASK 0x000000f8 --#define CSI_SKIP_SMFC_SHIFT 3 --#define CSI_ID_2_SKIP_MASK 0x00000300 --#define CSI_ID_2_SKIP_SHIFT 8 -- --#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 --#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -- --/* MIPI CSI-2 data types */ --#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ --#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ --#define MIPI_DT_YUV422 0x1e /* UYVY... */ --#define MIPI_DT_RGB444 0x20 --#define MIPI_DT_RGB555 0x21 --#define MIPI_DT_RGB565 0x22 --#define MIPI_DT_RGB666 0x23 --#define MIPI_DT_RGB888 0x24 --#define MIPI_DT_RAW6 0x28 --#define MIPI_DT_RAW7 0x29 --#define MIPI_DT_RAW8 0x2a --#define MIPI_DT_RAW10 0x2b --#define MIPI_DT_RAW12 0x2c --#define MIPI_DT_RAW14 0x2d -- --/* -- * Bitfield of CSI bus signal polarities and modes. -- */ --struct ipu_csi_bus_config { -- unsigned data_width:4; -- unsigned clk_mode:3; -- unsigned ext_vsync:1; -- unsigned vsync_pol:1; -- unsigned hsync_pol:1; -- unsigned pixclk_pol:1; -- unsigned data_pol:1; -- unsigned sens_clksrc:1; -- unsigned pack_tight:1; -- unsigned force_eof:1; -- unsigned data_en_pol:1; -- -- unsigned data_fmt; -- unsigned mipi_dt; --}; -- --/* -- * Enumeration of CSI data bus widths. -- */ --enum ipu_csi_data_width { -- IPU_CSI_DATA_WIDTH_4 = 0, -- IPU_CSI_DATA_WIDTH_8 = 1, -- IPU_CSI_DATA_WIDTH_10 = 3, -- IPU_CSI_DATA_WIDTH_12 = 5, -- IPU_CSI_DATA_WIDTH_16 = 9, --}; -- --/* -- * Enumeration of CSI clock modes. -- */ --enum ipu_csi_clk_mode { -- IPU_CSI_CLK_MODE_GATED_CLK, -- IPU_CSI_CLK_MODE_NONGATED_CLK, -- IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -- IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, --}; -- --static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) --{ -- return readl(csi->base + offset); --} -- --static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -- unsigned offset) --{ -- writel(value, csi->base + offset); --} -- --/* -- * Set mclk division ratio for generating test mode mclk. Only used -- * for test generator. -- */ --static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -- u32 ipu_clk) --{ -- u32 temp; -- int div_ratio; -- -- div_ratio = (ipu_clk / pixel_clk) - 1; -- -- if (div_ratio > 0xFF || div_ratio < 0) { -- dev_err(csi->ipu->dev, -- "value of pixel_clk extends normal range\n"); -- return -EINVAL; -- } -- -- temp = ipu_csi_read(csi, CSI_SENS_CONF); -- temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -- ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -- CSI_SENS_CONF); -- -- return 0; --} -- --/* -- * Find the CSI data format and data width for the given V4L2 media -- * bus pixel format code. -- */ --static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -- enum v4l2_mbus_type mbus_type) --{ -- switch (mbus_code) { -- case MEDIA_BUS_FMT_BGR565_2X8_BE: -- case MEDIA_BUS_FMT_BGR565_2X8_LE: -- case MEDIA_BUS_FMT_RGB565_2X8_BE: -- case MEDIA_BUS_FMT_RGB565_2X8_LE: -- if (mbus_type == V4L2_MBUS_CSI2_DPHY) -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -- else -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RGB565; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -- cfg->mipi_dt = MIPI_DT_RGB444; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -- cfg->mipi_dt = MIPI_DT_RGB555; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB888_1X24: -- case MEDIA_BUS_FMT_BGR888_1X24: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -- cfg->mipi_dt = MIPI_DT_RGB888; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_YUYV8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_16; -- break; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- case MEDIA_BUS_FMT_Y8_1X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- case MEDIA_BUS_FMT_Y10_1X10: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_10; -- break; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- case MEDIA_BUS_FMT_Y12_1X12: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW12; -- cfg->data_width = IPU_CSI_DATA_WIDTH_12; -- break; -- case MEDIA_BUS_FMT_JPEG_1X8: -- /* TODO */ -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} -- --/* translate alternate field mode based on given standard */ --static inline enum v4l2_field --ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) --{ -- return (field != V4L2_FIELD_ALTERNATE) ? field : -- ((std & V4L2_STD_525_60) ? -- V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); --} -- --/* -- * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -- */ --static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *mbus_fmt) --{ -- int ret; -- -- memset(csicfg, 0, sizeof(*csicfg)); -- -- ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -- if (ret < 0) -- return ret; -- -- switch (mbus_cfg->type) { -- case V4L2_MBUS_PARALLEL: -- csicfg->ext_vsync = 1; -- csicfg->vsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->hsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->pixclk_pol = (mbus_cfg->flags & -- V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -- csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -- break; -- case V4L2_MBUS_BT656: -- csicfg->ext_vsync = 0; -- if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -- mbus_fmt->field == V4L2_FIELD_ALTERNATE) -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -- else -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -- break; -- case V4L2_MBUS_CSI2_DPHY: -- /* -- * MIPI CSI-2 requires non gated clock mode, all other -- * parameters are not applicable for MIPI CSI-2 bus. -- */ -- csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -- break; -- default: -- /* will never get here, keep compiler quiet */ -- break; -- } -- -- return 0; --} -- --static int --ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt, -- v4l2_std_id std) --{ -- enum v4l2_field infield, outfield; -- bool swap_fields; -- -- /* get translated field type of input and output */ -- infield = ipu_csi_translate_field(infmt->field, std); -- outfield = ipu_csi_translate_field(outfmt->field, std); -- -- /* -- * Write the H-V-F codes the CSI will match against the -- * incoming data for start/end of active and blanking -- * field intervals. If input and output field types are -- * sequential but not the same (one is SEQ_BT and the other -- * is SEQ_TB), swap the F-bit so that the CSI will capture -- * field 1 lines before field 0 lines. -- */ -- swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -- V4L2_FIELD_IS_SEQUENTIAL(outfield) && -- infield != outfield); -- -- if (!swap_fields) { -- /* -- * Field0BlankEnd = 110, Field0BlankStart = 010 -- * Field0ActiveEnd = 100, Field0ActiveStart = 000 -- * Field1BlankEnd = 111, Field1BlankStart = 011 -- * Field1ActiveEnd = 101, Field1ActiveStart = 001 -- */ -- ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -- } else { -- dev_dbg(csi->ipu->dev, "capture field swap\n"); -- -- /* same as above but with F-bit inverted */ -- ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -- } -- -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- -- return 0; --} -- -- --int ipu_csi_init_interface(struct ipu_csi *csi, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 width, height, data = 0; -- v4l2_std_id std; -- int ret; -- -- ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -- if (ret < 0) -- return ret; -- -- /* set default sensor frame width and height */ -- width = infmt->width; -- height = infmt->height; -- if (infmt->field == V4L2_FIELD_ALTERNATE) -- height *= 2; -- -- /* Set the CSI_SENS_CONF register remaining fields */ -- data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -- cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -- cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -- cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -- cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -- cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -- cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -- cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -- cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -- cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -- cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, data, CSI_SENS_CONF); -- -- /* Set CCIR registers */ -- -- switch (cfg.clk_mode) { -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- if (width == 720 && height == 480) { -- std = V4L2_STD_NTSC; -- height = 525; -- } else if (width == 720 && height == 576) { -- std = V4L2_STD_PAL; -- height = 625; -- } else { -- dev_err(csi->ipu->dev, -- "Unsupported interlaced video mode\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -- if (ret) -- goto out_unlock; -- break; -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -- break; -- } -- -- /* Setup sensor frame size */ -- ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -- CSI_SENS_FRM_SIZE); -- -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- --out_unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -- --bool ipu_csi_is_interlaced(struct ipu_csi *csi) --{ -- unsigned long flags; -- u32 sensor_protocol; -- -- spin_lock_irqsave(&csi->lock, flags); -- sensor_protocol = -- (ipu_csi_read(csi, CSI_SENS_CONF) & -- CSI_SENS_CONF_SENS_PRTCL_MASK) >> -- CSI_SENS_CONF_SENS_PRTCL_SHIFT; -- spin_unlock_irqrestore(&csi->lock, flags); -- -- switch (sensor_protocol) { -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- return false; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- return true; -- default: -- dev_err(csi->ipu->dev, -- "CSI %d sensor protocol unsupported\n", csi->id); -- return false; -- } --} --EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -- --void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -- w->width = (reg & 0xFFFF) + 1; -- w->height = (reg >> 16 & 0xFFFF) + 1; -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -- w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_get_window); -- --void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -- CSI_ACT_FRM_SIZE); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -- reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_window); -- --void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -- reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -- (vert ? CSI_VERT_DOWNSIZE_EN : 0); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -- --void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -- u32 r_value, u32 g_value, u32 b_value, -- u32 pix_clk) --{ -- unsigned long flags; -- u32 ipu_clk = clk_get_rate(csi->clk_ipu); -- u32 temp; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_TST_CTRL); -- -- if (!active) { -- temp &= ~CSI_TEST_GEN_MODE_EN; -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } else { -- /* Set sensb_mclk div_ratio */ -- ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -- -- temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -- CSI_TEST_GEN_B_MASK); -- temp |= CSI_TEST_GEN_MODE_EN; -- temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -- (g_value << CSI_TEST_GEN_G_SHIFT) | -- (b_value << CSI_TEST_GEN_B_SHIFT); -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -- --int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -- struct v4l2_mbus_framefmt *mbus_fmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 temp; -- int ret; -- -- if (vc > 3) -- return -EINVAL; -- -- ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -- if (ret < 0) -- return ret; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_MIPI_DI); -- temp &= ~(0xff << (vc * 8)); -- temp |= (cfg.mipi_dt << (vc * 8)); -- ipu_csi_write(csi, temp, CSI_MIPI_DI); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -- --int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -- u32 max_ratio, u32 id) --{ -- unsigned long flags; -- u32 temp; -- -- if (max_ratio > 5 || id > 3) -- return -EINVAL; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_SKIP); -- temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -- CSI_SKIP_SMFC_MASK); -- temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -- (id << CSI_ID_2_SKIP_SHIFT) | -- (skip << CSI_SKIP_SMFC_SHIFT); -- ipu_csi_write(csi, temp, CSI_SKIP); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -- --int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) --{ -- unsigned long flags; -- u32 csi_sens_conf, dest; -- -- if (csi_dest == IPU_CSI_DEST_IDMAC) -- dest = CSI_DATA_DEST_IDMAC; -- else -- dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -- -- spin_lock_irqsave(&csi->lock, flags); -- -- csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -- csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -- csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -- ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -- --int ipu_csi_enable(struct ipu_csi *csi) --{ -- ipu_module_enable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_enable); -- --int ipu_csi_disable(struct ipu_csi *csi) --{ -- ipu_module_disable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_disable); -- --struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) --{ -- unsigned long flags; -- struct ipu_csi *csi, *ret; -- -- if (id > 1) -- return ERR_PTR(-EINVAL); -- -- csi = ipu->csi_priv[id]; -- ret = csi; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- if (csi->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- csi->inuse = true; --unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_get); -- --void ipu_csi_put(struct ipu_csi *csi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&csi->lock, flags); -- csi->inuse = false; -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_put); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu) --{ -- struct ipu_csi *csi; -- -- if (id > 1) -- return -ENODEV; -- -- csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -- if (!csi) -- return -ENOMEM; -- -- ipu->csi_priv[id] = csi; -- -- spin_lock_init(&csi->lock); -- csi->module = module; -- csi->id = id; -- csi->clk_ipu = clk_ipu; -- csi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!csi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -- id, base, csi->base); -- csi->ipu = ipu; -- -- return 0; --} -- --void ipu_csi_exit(struct ipu_soc *ipu, int id) --{ --} -- --void ipu_csi_dump(struct ipu_csi *csi) --{ -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_TST_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_1)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_2)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_3)); -- dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -- ipu_csi_read(csi, CSI_MIPI_DI)); -- dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -- ipu_csi_read(csi, CSI_SKIP)); --} --EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- a/drivers/gpu/ipu-v3/ipu-dc.c -+++ /dev/null -@@ -1,420 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ -- --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) --#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -- --#define DC_EVT_NF 0 --#define DC_EVT_NL 1 --#define DC_EVT_EOF 2 --#define DC_EVT_NFIELD 3 --#define DC_EVT_EOL 4 --#define DC_EVT_EOFIELD 5 --#define DC_EVT_NEW_ADDR 6 --#define DC_EVT_NEW_CHAN 7 --#define DC_EVT_NEW_DATA 8 -- --#define DC_EVT_NEW_ADDR_W_0 0 --#define DC_EVT_NEW_ADDR_W_1 1 --#define DC_EVT_NEW_CHAN_W_0 2 --#define DC_EVT_NEW_CHAN_W_1 3 --#define DC_EVT_NEW_DATA_W_0 4 --#define DC_EVT_NEW_DATA_W_1 5 --#define DC_EVT_NEW_ADDR_R_0 6 --#define DC_EVT_NEW_ADDR_R_1 7 --#define DC_EVT_NEW_CHAN_R_0 8 --#define DC_EVT_NEW_CHAN_R_1 9 --#define DC_EVT_NEW_DATA_R_0 10 --#define DC_EVT_NEW_DATA_R_1 11 -- --#define DC_WR_CH_CONF 0x0 --#define DC_WR_CH_ADDR 0x4 --#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -- --#define DC_GEN 0xd4 --#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) --#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) --#define DC_STAT 0x1c8 -- --#define WROD(lf) (0x18 | ((lf) << 1)) --#define WRG 0x01 --#define WCLK 0xc9 -- --#define SYNC_WAVE 0 --#define NULL_WAVE (-1) -- --#define DC_GEN_SYNC_1_6_SYNC (2 << 1) --#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -- --#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) --#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) --#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) --#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) --#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) --#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) --#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) --#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) --#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -- --#define IPU_DC_NUM_CHANNELS 10 -- --struct ipu_dc_priv; -- --enum ipu_dc_map { -- IPU_DC_MAP_RGB24, -- IPU_DC_MAP_RGB565, -- IPU_DC_MAP_GBR24, /* TVEv2 */ -- IPU_DC_MAP_BGR666, -- IPU_DC_MAP_LVDS666, -- IPU_DC_MAP_BGR24, --}; -- --struct ipu_dc { -- /* The display interface number assigned to this dc channel */ -- unsigned int di; -- void __iomem *base; -- struct ipu_dc_priv *priv; -- int chno; -- bool in_use; --}; -- --struct ipu_dc_priv { -- void __iomem *dc_reg; -- void __iomem *dc_tmpl_reg; -- struct ipu_soc *ipu; -- struct device *dev; -- struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -- struct mutex mutex; -- struct completion comp; -- int use_count; --}; -- --static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_RL_CH(event)); -- reg &= ~(0xffff << (16 * (event & 0x1))); -- reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -- writel(reg, dc->base + DC_RL_CH(event)); --} -- --static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -- int map, int wave, int glue, int sync, int stop) --{ -- struct ipu_dc_priv *priv = dc->priv; -- u32 reg1, reg2; -- -- if (opcode == WCLK) { -- reg1 = (operand << 20) & 0xfff00000; -- reg2 = operand >> 12 | opcode << 1 | stop << 9; -- } else if (opcode == WRG) { -- reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -- reg2 = operand >> 17 | opcode << 7 | stop << 9; -- } else { -- reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -- reg2 = operand >> 12 | opcode << 4 | stop << 9; -- } -- writel(reg1, priv->dc_tmpl_reg + word * 8); -- writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); --} -- --static int ipu_bus_format_to_map(u32 fmt) --{ -- switch (fmt) { -- default: -- WARN_ON(1); -- /* fall-through */ -- case MEDIA_BUS_FMT_RGB888_1X24: -- return IPU_DC_MAP_RGB24; -- case MEDIA_BUS_FMT_RGB565_1X16: -- return IPU_DC_MAP_RGB565; -- case MEDIA_BUS_FMT_GBR888_1X24: -- return IPU_DC_MAP_GBR24; -- case MEDIA_BUS_FMT_RGB666_1X18: -- return IPU_DC_MAP_BGR666; -- case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -- return IPU_DC_MAP_LVDS666; -- case MEDIA_BUS_FMT_BGR888_1X24: -- return IPU_DC_MAP_BGR24; -- } --} -- --int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -- u32 bus_format, u32 width) --{ -- struct ipu_dc_priv *priv = dc->priv; -- int addr, sync; -- u32 reg = 0; -- int map; -- -- dc->di = ipu_di_get_num(di); -- -- map = ipu_bus_format_to_map(bus_format); -- -- /* -- * In interlaced mode we need more counters to create the asymmetric -- * per-field VSYNC signals. The pixel active signal synchronising DC -- * to DI moves to signal generator #6 (see ipu-di.c). In progressive -- * mode counter #5 is used. -- */ -- sync = interlaced ? 6 : 5; -- -- /* Reserve 5 microcode template words for each DI */ -- if (dc->di) -- addr = 5; -- else -- addr = 0; -- -- if (interlaced) { -- dc_link_event(dc, DC_EVT_NL, addr, 3); -- dc_link_event(dc, DC_EVT_EOL, addr, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } else { -- dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -- dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -- dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -- dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -- dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } -- -- dc_link_event(dc, DC_EVT_NF, 0, 0); -- dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_EOF, 0, 0); -- dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- if (interlaced) -- reg |= DC_WR_CH_CONF_FIELD_MODE; -- else -- reg &= ~DC_WR_CH_CONF_FIELD_MODE; -- writel(reg, dc->base + DC_WR_CH_CONF); -- -- writel(0x0, dc->base + DC_WR_CH_ADDR); -- writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -- --void ipu_dc_enable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable); -- --void ipu_dc_enable_channel(struct ipu_dc *dc) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -- writel(reg, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -- --void ipu_dc_disable_channel(struct ipu_dc *dc) --{ -- u32 val; -- -- val = readl(dc->base + DC_WR_CH_CONF); -- val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -- writel(val, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -- --void ipu_dc_disable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable); -- --static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -- int byte_num, int offset, int mask) --{ -- int ptr = map * 3 + byte_num; -- u32 reg; -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- reg &= ~(0xffff << (16 * (ptr & 0x1))); -- reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -- reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) --{ -- u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- -- writel(reg & ~(0xffff << (16 * (map & 0x1))), -- priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- struct ipu_dc *dc; -- -- if (channel >= IPU_DC_NUM_CHANNELS) -- return ERR_PTR(-ENODEV); -- -- dc = &priv->channels[channel]; -- -- mutex_lock(&priv->mutex); -- -- if (dc->in_use) { -- mutex_unlock(&priv->mutex); -- return ERR_PTR(-EBUSY); -- } -- -- dc->in_use = true; -- -- mutex_unlock(&priv->mutex); -- -- return dc; --} --EXPORT_SYMBOL_GPL(ipu_dc_get); -- --void ipu_dc_put(struct ipu_dc *dc) --{ -- struct ipu_dc_priv *priv = dc->priv; -- -- mutex_lock(&priv->mutex); -- dc->in_use = false; -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_put); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long template_base) --{ -- struct ipu_dc_priv *priv; -- static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -- 0x78, 0, 0x94, 0xb4}; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- priv->dev = dev; -- priv->ipu = ipu; -- priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -- priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -- if (!priv->dc_reg || !priv->dc_tmpl_reg) -- return -ENOMEM; -- -- for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -- priv->channels[i].chno = i; -- priv->channels[i].priv = priv; -- priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -- } -- -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -- DC_WR_CH_CONF_PROG_DI_ID, -- priv->channels[1].base + DC_WR_CH_CONF); -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -- priv->channels[5].base + DC_WR_CH_CONF); -- -- writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -- priv->dc_reg + DC_GEN); -- -- ipu->dc_priv = priv; -- -- dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -- base, template_base); -- -- /* rgb24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -- -- /* rgb565 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -- -- /* gbr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -- -- /* bgr666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -- -- /* lvds666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -- -- /* bgr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -- -- return 0; --} -- --void ipu_dc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-di.c -+++ /dev/null -@@ -1,745 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --struct ipu_di { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_di; /* display input clock */ -- struct clk *clk_ipu; /* IPU bus clock */ -- struct clk *clk_di_pixel; /* resulting pixel clock */ -- bool inuse; -- struct ipu_soc *ipu; --}; -- --static DEFINE_MUTEX(di_mutex); -- --struct di_sync_config { -- int run_count; -- int run_src; -- int offset_count; -- int offset_src; -- int repeat_count; -- int cnt_clr_src; -- int cnt_polarity_gen_en; -- int cnt_polarity_clr_src; -- int cnt_polarity_trigger_src; -- int cnt_up; -- int cnt_down; --}; -- --enum di_pins { -- DI_PIN11 = 0, -- DI_PIN12 = 1, -- DI_PIN13 = 2, -- DI_PIN14 = 3, -- DI_PIN15 = 4, -- DI_PIN16 = 5, -- DI_PIN17 = 6, -- DI_PIN_CS = 7, -- -- DI_PIN_SER_CLK = 0, -- DI_PIN_SER_RS = 1, --}; -- --enum di_sync_wave { -- DI_SYNC_NONE = 0, -- DI_SYNC_CLK = 1, -- DI_SYNC_INT_HSYNC = 2, -- DI_SYNC_HSYNC = 3, -- DI_SYNC_VSYNC = 4, -- DI_SYNC_DE = 6, -- -- DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -- DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -- DI_SYNC_CNT5 = 6, /* counter >= 6 only */ --}; -- --#define SYNC_WAVE 0 -- --#define DI_GENERAL 0x0000 --#define DI_BS_CLKGEN0 0x0004 --#define DI_BS_CLKGEN1 0x0008 --#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) --#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) --#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) --#define DI_SYNC_AS_GEN 0x0054 --#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) --#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) --#define DI_SER_CONF 0x015c --#define DI_SSC 0x0160 --#define DI_POL 0x0164 --#define DI_AW0 0x0168 --#define DI_AW1 0x016c --#define DI_SCR_CONF 0x0170 --#define DI_STAT 0x0174 -- --#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) --#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) --#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) --#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -- --#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) --#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) --#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) --#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) --#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) --#define DI_SW_GEN1_CNT_UP(x) (x) --#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -- --#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 --#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -- --#define DI_GEN_POLARITY_1 (1 << 0) --#define DI_GEN_POLARITY_2 (1 << 1) --#define DI_GEN_POLARITY_3 (1 << 2) --#define DI_GEN_POLARITY_4 (1 << 3) --#define DI_GEN_POLARITY_5 (1 << 4) --#define DI_GEN_POLARITY_6 (1 << 5) --#define DI_GEN_POLARITY_7 (1 << 6) --#define DI_GEN_POLARITY_8 (1 << 7) --#define DI_GEN_POLARITY_DISP_CLK (1 << 17) --#define DI_GEN_DI_CLK_EXT (1 << 20) --#define DI_GEN_DI_VSYNC_EXT (1 << 21) -- --#define DI_POL_DRDY_DATA_POLARITY (1 << 7) --#define DI_POL_DRDY_POLARITY_15 (1 << 4) -- --#define DI_VSYNC_SEL_OFFSET 13 -- --static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) --{ -- return readl(di->base + offset); --} -- --static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) --{ -- writel(value, di->base + offset); --} -- --static void ipu_di_data_wave_config(struct ipu_di *di, -- int wave_gen, -- int access_size, int component_size) --{ -- u32 reg; -- reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -- (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); --} -- --static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -- int set, int up, int down) --{ -- u32 reg; -- -- reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -- reg &= ~(0x3 << (di_pin * 2)); -- reg |= set << (di_pin * 2); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -- -- ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); --} -- --static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -- int start, int count) --{ -- u32 reg; -- int i; -- -- for (i = 0; i < count; i++) { -- struct di_sync_config *c = &config[i]; -- int wave_gen = start + i + 1; -- -- if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -- (c->repeat_count >= 0x1000) || -- (c->cnt_up >= 0x400) || -- (c->cnt_down >= 0x400)) { -- dev_err(di->ipu->dev, "DI%d counters out of range.\n", -- di->id); -- return; -- } -- -- reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -- DI_SW_GEN0_RUN_SRC(c->run_src) | -- DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -- DI_SW_GEN0_OFFSET_SRC(c->offset_src); -- ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -- -- reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -- DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -- DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -- c->cnt_polarity_trigger_src) | -- DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -- DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -- DI_SW_GEN1_CNT_UP(c->cnt_up); -- -- /* Enable auto reload */ -- if (c->repeat_count == 0) -- reg |= DI_SW_GEN1_AUTO_RELOAD; -- -- ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -- -- reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -- reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -- reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -- ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -- } --} -- --static void ipu_di_sync_config_interlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: internal VSYNC for each frame */ -- .run_count = v_total * 2 - 1, -- .run_src = 3, /* == counter 7 */ -- }, { -- /* PIN2: HSYNC waveform */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- }, { -- /* PIN3: VSYNC waveform */ -- .run_count = v_total - 1, -- .run_src = 4, /* == counter 7 */ -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = 4, /* == counter 7 */ -- .cnt_down = sig->mode.vsync_len * 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 4: Field */ -- .run_count = v_total / 2, -- .run_src = DI_SYNC_HSYNC, -- .offset_count = h_total / 2, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 5: Active lines */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = (sig->mode.vsync_len + -- sig->mode.vback_porch) / 2, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive / 2, -- .cnt_clr_src = DI_SYNC_CNT4, -- }, { -- /* 6: Active pixel, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = DI_SYNC_CNT5, -- }, { -- /* 7: Half line HSYNC */ -- .run_count = h_total / 2 - 1, -- .run_src = DI_SYNC_CLK, -- } -- }; -- -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- -- ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); --} -- --static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig, int div) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* PIN2: HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync, -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN3: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* 4: Line Active */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = DI_SYNC_VSYNC, -- } , { -- /* 5: Pixel Active, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 5, /* Line Active */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- }, -- }; -- /* can't use #7 and #8 for line active and pixel active counters */ -- struct di_sync_config cfg_vga[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* 2: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- } , { -- /* 3: Line Active */ -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_INT_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = 3 /* VSYNC */, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* 5: Pixel Active signal to DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 4, /* Line Active */ -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* unused */ -- }, -- }; -- -- ipu_di_write(di, v_total - 1, DI_SCR_CONF); -- if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- else -- ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); --} -- --static void ipu_di_config_clock(struct ipu_di *di, -- const struct ipu_di_signal_cfg *sig) --{ -- struct clk *clk; -- unsigned clkgen0; -- uint32_t val; -- -- if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -- /* -- * CLKMODE_EXT means we must use the DI clock: this is -- * needed for things like LVDS which needs to feed the -- * DI and LDB with the same pixel clock. -- */ -- clk = di->clk_di; -- -- if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -- /* -- * CLKMODE_SYNC means that we want the DI to be -- * clocked at the same rate as the parent clock. -- * This is needed (eg) for LDB which needs to be -- * fed with the same pixel clock. We assume that -- * the LDB clock has already been set correctly. -- */ -- clkgen0 = 1 << 4; -- } else { -- /* -- * We can use the divider. We should really have -- * a flag here indicating whether the bridge can -- * cope with a fractional divider or not. For the -- * time being, let's go for simplicitly and -- * reliability. -- */ -- unsigned long in_rate; -- unsigned div; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } else { -- /* -- * For other interfaces, we can arbitarily select between -- * the DI specific clock and the internal IPU clock. See -- * DI_GENERAL bit 20. We select the IPU clock if it can -- * give us a clock rate within 1% of the requested frequency, -- * otherwise we use the DI clock. -- */ -- unsigned long rate, clkrate; -- unsigned div, error; -- -- clkrate = clk_get_rate(di->clk_ipu); -- div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- rate = clkrate / div; -- -- error = rate / (sig->mode.pixelclock / 1000); -- -- dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -- rate, div, (signed)(error - 1000) / 10, error % 10); -- -- /* Allow a 1% error */ -- if (error < 1010 && error >= 990) { -- clk = di->clk_ipu; -- -- clkgen0 = div << 4; -- } else { -- unsigned long in_rate; -- unsigned div; -- -- clk = di->clk_di; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } -- -- di->clk_di_pixel = clk; -- -- /* Set the divider */ -- ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -- -- /* -- * Set the high/low periods. Bits 24:16 give us the falling edge, -- * and bits 8:0 give the rising edge. LSB is fraction, and is -- * based on the divider above. We want a 50% duty cycle, so set -- * the falling edge to be half the divider. -- */ -- ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -- -- /* Finally select the input clock */ -- val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -- if (clk == di->clk_di) -- val |= DI_GEN_DI_CLK_EXT; -- ipu_di_write(di, val, DI_GENERAL); -- -- dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -- sig->mode.pixelclock, -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- clk == di->clk_di ? "DI" : "IPU", -- clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); --} -- --/* -- * This function is called to adjust a video mode to IPU restrictions. -- * It is meant to be called from drm crtc mode_fixup() methods. -- */ --int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) --{ -- u32 diff; -- -- if (mode->vfront_porch >= 2) -- return 0; -- -- diff = 2 - mode->vfront_porch; -- -- if (mode->vback_porch >= diff) { -- mode->vfront_porch = 2; -- mode->vback_porch -= diff; -- } else if (mode->vsync_len > diff) { -- mode->vfront_porch = 2; -- mode->vsync_len = mode->vsync_len - diff; -- } else { -- dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -- return -EINVAL; -- } -- -- dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -- --static u32 ipu_di_gen_polarity(int pin) --{ -- switch (pin) { -- case 1: -- return DI_GEN_POLARITY_1; -- case 2: -- return DI_GEN_POLARITY_2; -- case 3: -- return DI_GEN_POLARITY_3; -- case 4: -- return DI_GEN_POLARITY_4; -- case 5: -- return DI_GEN_POLARITY_5; -- case 6: -- return DI_GEN_POLARITY_6; -- case 7: -- return DI_GEN_POLARITY_7; -- case 8: -- return DI_GEN_POLARITY_8; -- } -- return 0; --} -- --int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) --{ -- u32 reg; -- u32 di_gen, vsync_cnt; -- u32 div; -- -- dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -- di->id, sig->mode.hactive, sig->mode.vactive); -- -- dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- sig->mode.pixelclock); -- -- mutex_lock(&di_mutex); -- -- ipu_di_config_clock(di, sig); -- -- div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -- div = div / 16; /* Now divider is integer portion */ -- -- /* Setup pixel clock timing */ -- /* Down time is half of period */ -- ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -- -- ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -- ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -- -- di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -- di_gen |= DI_GEN_DI_VSYNC_EXT; -- -- if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -- ipu_di_sync_config_interlaced(di, sig); -- -- /* set y_sel = 1 */ -- di_gen |= 0x10000000; -- -- vsync_cnt = 3; -- } else { -- ipu_di_sync_config_noninterlaced(di, sig, div); -- -- vsync_cnt = 3; -- if (di->id == 1) -- /* -- * TODO: change only for TVEv2, parallel display -- * uses pin 2 / 3 -- */ -- if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -- vsync_cnt = 6; -- } -- -- if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -- if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -- -- if (sig->clk_pol) -- di_gen |= DI_GEN_POLARITY_DISP_CLK; -- -- ipu_di_write(di, di_gen, DI_GENERAL); -- -- ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -- DI_SYNC_AS_GEN); -- -- reg = ipu_di_read(di, DI_POL); -- reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -- -- if (sig->enable_pol) -- reg |= DI_POL_DRDY_POLARITY_15; -- if (sig->data_pol) -- reg |= DI_POL_DRDY_DATA_POLARITY; -- -- ipu_di_write(di, reg, DI_POL); -- -- mutex_unlock(&di_mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -- --int ipu_di_enable(struct ipu_di *di) --{ -- int ret; -- -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ret = clk_prepare_enable(di->clk_di_pixel); -- if (ret) -- return ret; -- -- ipu_module_enable(di->ipu, di->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_enable); -- --int ipu_di_disable(struct ipu_di *di) --{ -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ipu_module_disable(di->ipu, di->module); -- -- clk_disable_unprepare(di->clk_di_pixel); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_disable); -- --int ipu_di_get_num(struct ipu_di *di) --{ -- return di->id; --} --EXPORT_SYMBOL_GPL(ipu_di_get_num); -- --static DEFINE_MUTEX(ipu_di_lock); -- --struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) --{ -- struct ipu_di *di; -- -- if (disp > 1) -- return ERR_PTR(-EINVAL); -- -- di = ipu->di_priv[disp]; -- -- mutex_lock(&ipu_di_lock); -- -- if (di->inuse) { -- di = ERR_PTR(-EBUSY); -- goto out; -- } -- -- di->inuse = true; --out: -- mutex_unlock(&ipu_di_lock); -- -- return di; --} --EXPORT_SYMBOL_GPL(ipu_di_get); -- --void ipu_di_put(struct ipu_di *di) --{ -- mutex_lock(&ipu_di_lock); -- -- di->inuse = false; -- -- mutex_unlock(&ipu_di_lock); --} --EXPORT_SYMBOL_GPL(ipu_di_put); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, -- u32 module, struct clk *clk_ipu) --{ -- struct ipu_di *di; -- -- if (id > 1) -- return -ENODEV; -- -- di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -- if (!di) -- return -ENOMEM; -- -- ipu->di_priv[id] = di; -- -- di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -- if (IS_ERR(di->clk_di)) -- return PTR_ERR(di->clk_di); -- -- di->module = module; -- di->id = id; -- di->clk_ipu = clk_ipu; -- di->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!di->base) -- return -ENOMEM; -- -- ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -- -- dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -- id, base, di->base); -- di->inuse = false; -- di->ipu = ipu; -- -- return 0; --} -- --void ipu_di_exit(struct ipu_soc *ipu, int id) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-dmfc.c -+++ /dev/null -@@ -1,214 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DMFC_RD_CHAN 0x0000 --#define DMFC_WR_CHAN 0x0004 --#define DMFC_WR_CHAN_DEF 0x0008 --#define DMFC_DP_CHAN 0x000c --#define DMFC_DP_CHAN_DEF 0x0010 --#define DMFC_GENERAL1 0x0014 --#define DMFC_GENERAL2 0x0018 --#define DMFC_IC_CTRL 0x001c --#define DMFC_WR_CHAN_ALT 0x0020 --#define DMFC_WR_CHAN_DEF_ALT 0x0024 --#define DMFC_DP_CHAN_ALT 0x0028 --#define DMFC_DP_CHAN_DEF_ALT 0x002c --#define DMFC_GENERAL1_ALT 0x0030 --#define DMFC_STAT 0x0034 -- --#define DMFC_WR_CHAN_1_28 0 --#define DMFC_WR_CHAN_2_41 8 --#define DMFC_WR_CHAN_1C_42 16 --#define DMFC_WR_CHAN_2C_43 24 -- --#define DMFC_DP_CHAN_5B_23 0 --#define DMFC_DP_CHAN_5F_27 8 --#define DMFC_DP_CHAN_6B_24 16 --#define DMFC_DP_CHAN_6F_29 24 -- --struct dmfc_channel_data { -- int ipu_channel; -- unsigned long channel_reg; -- unsigned long shift; -- unsigned eot_shift; -- unsigned max_fifo_lines; --}; -- --static const struct dmfc_channel_data dmfcdata[] = { -- { -- .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5B_23, -- .eot_shift = 20, -- .max_fifo_lines = 3, -- }, { -- .ipu_channel = 24, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6B_24, -- .eot_shift = 22, -- .max_fifo_lines = 1, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5F_27, -- .eot_shift = 21, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -- .channel_reg = DMFC_WR_CHAN, -- .shift = DMFC_WR_CHAN_1_28, -- .eot_shift = 16, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = 29, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6F_29, -- .eot_shift = 23, -- .max_fifo_lines = 1, -- }, --}; -- --#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -- --struct ipu_dmfc_priv; -- --struct dmfc_channel { -- unsigned slots; -- struct ipu_soc *ipu; -- struct ipu_dmfc_priv *priv; -- const struct dmfc_channel_data *data; --}; -- --struct ipu_dmfc_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -- struct mutex mutex; -- void __iomem *base; -- int use_count; --}; -- --int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -- --void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -- --void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- u32 dmfc_gen1; -- -- mutex_lock(&priv->mutex); -- -- dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -- -- if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -- dmfc_gen1 |= 1 << dmfc->data->eot_shift; -- else -- dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -- -- writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -- --struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) --{ -- struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -- int i; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) -- if (dmfcdata[i].ipu_channel == ipu_channel) -- return &priv->channels[i]; -- return ERR_PTR(-ENODEV); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_get); -- --void ipu_dmfc_put(struct dmfc_channel *dmfc) --{ --} --EXPORT_SYMBOL_GPL(ipu_dmfc_put); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk) --{ -- struct ipu_dmfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- priv->dev = dev; -- priv->ipu = ipu; -- mutex_init(&priv->mutex); -- -- ipu->dmfc_priv = priv; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -- priv->channels[i].priv = priv; -- priv->channels[i].ipu = ipu; -- priv->channels[i].data = &dmfcdata[i]; -- -- if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -- priv->channels[i].slots = 2; -- } -- -- writel(0x00000050, priv->base + DMFC_WR_CHAN); -- writel(0x00005654, priv->base + DMFC_DP_CHAN); -- writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -- writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -- writel(0x00000003, priv->base + DMFC_GENERAL1); -- -- return 0; --} -- --void ipu_dmfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-dp.c -+++ /dev/null -@@ -1,357 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/kernel.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DP_SYNC 0 --#define DP_ASYNC0 0x60 --#define DP_ASYNC1 0xBC -- --#define DP_COM_CONF 0x0 --#define DP_GRAPH_WIND_CTRL 0x0004 --#define DP_FG_POS 0x0008 --#define DP_CSC_A_0 0x0044 --#define DP_CSC_A_1 0x0048 --#define DP_CSC_A_2 0x004C --#define DP_CSC_A_3 0x0050 --#define DP_CSC_0 0x0054 --#define DP_CSC_1 0x0058 -- --#define DP_COM_CONF_FG_EN (1 << 0) --#define DP_COM_CONF_GWSEL (1 << 1) --#define DP_COM_CONF_GWAM (1 << 2) --#define DP_COM_CONF_GWCKE (1 << 3) --#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) --#define DP_COM_CONF_CSC_DEF_OFFSET 8 --#define DP_COM_CONF_CSC_DEF_FG (3 << 8) --#define DP_COM_CONF_CSC_DEF_BG (2 << 8) --#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -- --#define IPUV3_NUM_FLOWS 3 -- --struct ipu_dp_priv; -- --struct ipu_dp { -- u32 flow; -- bool in_use; -- bool foreground; -- enum ipu_color_space in_cs; --}; -- --struct ipu_flow { -- struct ipu_dp foreground; -- struct ipu_dp background; -- enum ipu_color_space out_cs; -- void __iomem *base; -- struct ipu_dp_priv *priv; --}; -- --struct ipu_dp_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- void __iomem *base; -- struct ipu_flow flow[IPUV3_NUM_FLOWS]; -- struct mutex mutex; -- int use_count; --}; -- --static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -- --static inline struct ipu_flow *to_flow(struct ipu_dp *dp) --{ -- if (dp->foreground) -- return container_of(dp, struct ipu_flow, foreground); -- else -- return container_of(dp, struct ipu_flow, background); --} -- --int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -- u8 alpha, bool bg_chan) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- if (bg_chan) -- reg &= ~DP_COM_CONF_GWSEL; -- else -- reg |= DP_COM_CONF_GWSEL; -- writel(reg, flow->base + DP_COM_CONF); -- -- if (enable) { -- reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -- writel(reg | ((u32) alpha << 24), -- flow->base + DP_GRAPH_WIND_CTRL); -- -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } else { -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -- --int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -- --static void ipu_dp_csc_init(struct ipu_flow *flow, -- enum ipu_color_space in, -- enum ipu_color_space out, -- u32 place) --{ -- u32 reg; -- -- reg = readl(flow->base + DP_COM_CONF); -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- -- if (in == out) { -- writel(reg, flow->base + DP_COM_CONF); -- return; -- } -- -- if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -- writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -- writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -- writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -- writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -- writel(0x3d6 | (0x0000 << 16) | (2 << 30), -- flow->base + DP_CSC_0); -- writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -- flow->base + DP_CSC_1); -- } else { -- writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -- writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -- writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -- writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -- writel(0x000 | (0x3e42 << 16) | (1 << 30), -- flow->base + DP_CSC_0); -- writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -- flow->base + DP_CSC_1); -- } -- -- reg |= place; -- -- writel(reg, flow->base + DP_COM_CONF); --} -- --int ipu_dp_setup_channel(struct ipu_dp *dp, -- enum ipu_color_space in, -- enum ipu_color_space out) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- mutex_lock(&priv->mutex); -- -- dp->in_cs = in; -- -- if (!dp->foreground) -- flow->out_cs = out; -- -- if (flow->foreground.in_cs == flow->background.in_cs) { -- /* -- * foreground and background are of same colorspace, put -- * colorspace converter after combining unit. -- */ -- ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -- DP_COM_CONF_CSC_DEF_BOTH); -- } else { -- if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -- flow->foreground.in_cs == flow->out_cs) -- /* -- * foreground identical to output, apply color -- * conversion on background -- */ -- ipu_dp_csc_init(flow, flow->background.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -- else -- ipu_dp_csc_init(flow, flow->foreground.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -- --int ipu_dp_enable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable); -- --int ipu_dp_enable_channel(struct ipu_dp *dp) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- if (!dp->foreground) -- return 0; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- reg |= DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -- --void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg, csc; -- -- dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -- -- if (!dp->foreground) -- return; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- csc = reg & DP_COM_CONF_CSC_DEF_MASK; -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -- reg |= DP_COM_CONF_CSC_DEF_BG; -- -- reg &= ~DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- writel(0, flow->base + DP_FG_POS); -- ipu_srm_dp_update(priv->ipu, sync); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -- --void ipu_dp_disable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable); -- --struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- struct ipu_dp *dp; -- -- if ((flow >> 1) >= IPUV3_NUM_FLOWS) -- return ERR_PTR(-EINVAL); -- -- if (flow & 1) -- dp = &priv->flow[flow >> 1].foreground; -- else -- dp = &priv->flow[flow >> 1].background; -- -- if (dp->in_use) -- return ERR_PTR(-EBUSY); -- -- dp->in_use = true; -- -- return dp; --} --EXPORT_SYMBOL_GPL(ipu_dp_get); -- --void ipu_dp_put(struct ipu_dp *dp) --{ -- dp->in_use = false; --} --EXPORT_SYMBOL_GPL(ipu_dp_put); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_dp_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- priv->dev = dev; -- priv->ipu = ipu; -- -- ipu->dp_priv = priv; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -- priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.foreground = true; -- priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -- priv->flow[i].priv = priv; -- } -- -- return 0; --} -- --void ipu_dp_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-ic.c -+++ /dev/null -@@ -1,761 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ -- --#include <linux/types.h> --#include <linux/init.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/bitrev.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/sizes.h> --#include "ipu-prv.h" -- --/* IC Register Offsets */ --#define IC_CONF 0x0000 --#define IC_PRP_ENC_RSC 0x0004 --#define IC_PRP_VF_RSC 0x0008 --#define IC_PP_RSC 0x000C --#define IC_CMBP_1 0x0010 --#define IC_CMBP_2 0x0014 --#define IC_IDMAC_1 0x0018 --#define IC_IDMAC_2 0x001C --#define IC_IDMAC_3 0x0020 --#define IC_IDMAC_4 0x0024 -- --/* IC Register Fields */ --#define IC_CONF_PRPENC_EN (1 << 0) --#define IC_CONF_PRPENC_CSC1 (1 << 1) --#define IC_CONF_PRPENC_ROT_EN (1 << 2) --#define IC_CONF_PRPVF_EN (1 << 8) --#define IC_CONF_PRPVF_CSC1 (1 << 9) --#define IC_CONF_PRPVF_CSC2 (1 << 10) --#define IC_CONF_PRPVF_CMB (1 << 11) --#define IC_CONF_PRPVF_ROT_EN (1 << 12) --#define IC_CONF_PP_EN (1 << 16) --#define IC_CONF_PP_CSC1 (1 << 17) --#define IC_CONF_PP_CSC2 (1 << 18) --#define IC_CONF_PP_CMB (1 << 19) --#define IC_CONF_PP_ROT_EN (1 << 20) --#define IC_CONF_IC_GLB_LOC_A (1 << 28) --#define IC_CONF_KEY_COLOR_EN (1 << 29) --#define IC_CONF_RWS_EN (1 << 30) --#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -- --#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) --#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) --#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) --#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) --#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) --#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) --#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) --#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) --#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) --#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 --#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) --#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 --#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) --#define IC_IDMAC_1_PP_ROT_OFFSET 17 --#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) --#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) --#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -- --#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) --#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 --#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) --#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 --#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) --#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -- --#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) --#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 --#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) --#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 --#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) --#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -- --struct ic_task_regoffs { -- u32 rsc; -- u32 tpmem_csc[2]; --}; -- --struct ic_task_bitfields { -- u32 ic_conf_en; -- u32 ic_conf_rot_en; -- u32 ic_conf_cmb_en; -- u32 ic_conf_csc1_en; -- u32 ic_conf_csc2_en; -- u32 ic_cmb_galpha_bit; --}; -- --static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .rsc = IC_PRP_ENC_RSC, -- .tpmem_csc = {0x2008, 0}, -- }, -- [IC_TASK_VIEWFINDER] = { -- .rsc = IC_PRP_VF_RSC, -- .tpmem_csc = {0x4028, 0x4040}, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .rsc = IC_PP_RSC, -- .tpmem_csc = {0x6060, 0x6078}, -- }, --}; -- --static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .ic_conf_en = IC_CONF_PRPENC_EN, -- .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -- .ic_conf_cmb_en = 0, /* NA */ -- .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -- .ic_conf_csc2_en = 0, /* NA */ -- .ic_cmb_galpha_bit = 0, /* NA */ -- }, -- [IC_TASK_VIEWFINDER] = { -- .ic_conf_en = IC_CONF_PRPVF_EN, -- .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -- .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -- .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -- .ic_cmb_galpha_bit = 0, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .ic_conf_en = IC_CONF_PP_EN, -- .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PP_CMB, -- .ic_conf_csc1_en = IC_CONF_PP_CSC1, -- .ic_conf_csc2_en = IC_CONF_PP_CSC2, -- .ic_cmb_galpha_bit = 8, -- }, --}; -- --struct ipu_ic_priv; -- --struct ipu_ic { -- enum ipu_ic_task task; -- const struct ic_task_regoffs *reg; -- const struct ic_task_bitfields *bit; -- -- struct ipu_ic_colorspace in_cs; -- struct ipu_ic_colorspace g_in_cs; -- struct ipu_ic_colorspace out_cs; -- -- bool graphics; -- bool rotation; -- bool in_use; -- -- struct ipu_ic_priv *priv; --}; -- --struct ipu_ic_priv { -- void __iomem *base; -- void __iomem *tpmem_base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- int use_count; -- int irt_use_count; -- struct ipu_ic task[IC_NUM_TASKS]; --}; -- --static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) --{ -- return readl(ic->priv->base + offset); --} -- --static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) --{ -- writel(value, ic->priv->base + offset); --} -- --static int init_csc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int csc_index) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 __iomem *base; -- const u16 (*c)[3]; -- const u16 *a; -- u32 param; -- -- base = (u32 __iomem *) -- (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -- -- /* Cast to unsigned */ -- c = (const u16 (*)[3])csc->params.coeff; -- a = (const u16 *)csc->params.offset; -- -- param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -- ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -- writel(param, base++); -- -- param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -- (csc->params.sat << 10); -- writel(param, base++); -- -- param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -- writel(param, base++); -- -- param = ((a[1] & 0x1fe0) >> 5); -- writel(param, base++); -- -- param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -- ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -- writel(param, base++); -- -- param = ((a[2] & 0x1fe0) >> 5); -- writel(param, base++); -- -- return 0; --} -- --static int calc_resize_coeffs(struct ipu_ic *ic, -- u32 in_size, u32 out_size, -- u32 *resize_coeff, -- u32 *downsize_coeff) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 temp_size, temp_downsize; -- -- /* -- * Input size cannot be more than 4096, and output size cannot -- * be more than 1024 -- */ -- if (in_size > 4096) { -- dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -- return -EINVAL; -- } -- if (out_size > 1024) { -- dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -- return -EINVAL; -- } -- -- /* Cannot downsize more than 4:1 */ -- if ((out_size << 2) < in_size) { -- dev_err(ipu->dev, "Unsupported downsize\n"); -- return -EINVAL; -- } -- -- /* Compute downsizing coefficient */ -- temp_downsize = 0; -- temp_size = in_size; -- while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -- (temp_downsize < 2)) { -- temp_size >>= 1; -- temp_downsize++; -- } -- *downsize_coeff = temp_downsize; -- -- /* -- * compute resizing coefficient using the following equation: -- * resize_coeff = M * (SI - 1) / (SO - 1) -- * where M = 2^13, SI = input size, SO = output size -- */ -- *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -- if (*resize_coeff >= 16384L) { -- dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -- *resize_coeff = 0x3FFF; -- } -- -- return 0; --} -- --void ipu_ic_task_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf |= ic->bit->ic_conf_en; -- -- if (ic->rotation) -- ic_conf |= ic->bit->ic_conf_rot_en; -- -- if (ic->in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->graphics) { -- ic_conf |= ic->bit->ic_conf_cmb_en; -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->g_in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc2_en; -- } -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -- --void ipu_ic_task_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf &= ~(ic->bit->ic_conf_en | -- ic->bit->ic_conf_csc1_en | -- ic->bit->ic_conf_rot_en); -- if (ic->bit->ic_conf_csc2_en) -- ic_conf &= ~ic->bit->ic_conf_csc2_en; -- if (ic->bit->ic_conf_cmb_en) -- ic_conf &= ~ic->bit->ic_conf_cmb_en; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -- --int ipu_ic_task_graphics_init(struct ipu_ic *ic, -- const struct ipu_ic_colorspace *g_in_cs, -- bool galpha_en, u32 galpha, -- bool colorkey_en, u32 colorkey) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_ic_csc csc2; -- unsigned long flags; -- u32 reg, ic_conf; -- int ret = 0; -- -- if (ic->task == IC_TASK_ENCODER) -- return -EINVAL; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -- struct ipu_ic_csc csc1; -- -- ret = ipu_ic_calc_csc(&csc1, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB); -- if (ret) -- goto unlock; -- -- /* need transparent CSC1 conversion */ -- ret = init_csc(ic, &csc1, 0); -- if (ret) -- goto unlock; -- } -- -- ic->g_in_cs = *g_in_cs; -- csc2.in_cs = ic->g_in_cs; -- csc2.out_cs = ic->out_cs; -- -- ret = __ipu_ic_calc_csc(&csc2); -- if (ret) -- goto unlock; -- -- ret = init_csc(ic, &csc2, 1); -- if (ret) -- goto unlock; -- -- if (galpha_en) { -- ic_conf |= IC_CONF_IC_GLB_LOC_A; -- reg = ipu_ic_read(ic, IC_CMBP_1); -- reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -- reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -- ipu_ic_write(ic, reg, IC_CMBP_1); -- } else -- ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -- -- if (colorkey_en) { -- ic_conf |= IC_CONF_KEY_COLOR_EN; -- ipu_ic_write(ic, colorkey, IC_CMBP_2); -- } else -- ic_conf &= ~IC_CONF_KEY_COLOR_EN; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- ic->graphics = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -- --int ipu_ic_task_init_rsc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height, -- u32 rsc) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 downsize_coeff, resize_coeff; -- unsigned long flags; -- int ret = 0; -- -- if (!rsc) { -- /* Setup vertical resizing */ -- -- ret = calc_resize_coeffs(ic, in_height, out_height, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc = (downsize_coeff << 30) | (resize_coeff << 16); -- -- /* Setup horizontal resizing */ -- ret = calc_resize_coeffs(ic, in_width, out_width, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc |= (downsize_coeff << 14) | resize_coeff; -- } -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ipu_ic_write(ic, rsc, ic->reg->rsc); -- -- /* Setup color space conversion */ -- ic->in_cs = csc->in_cs; -- ic->out_cs = csc->out_cs; -- -- ret = init_csc(ic, csc, 0); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} -- --int ipu_ic_task_init(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height) --{ -- return ipu_ic_task_init_rsc(ic, csc, -- in_width, in_height, -- out_width, out_height, 0); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_init); -- --int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -- u32 width, u32 height, int burst_size, -- enum ipu_rotate_mode rot) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -- u32 temp_rot = bitrev8(rot) >> 5; -- bool need_hor_flip = false; -- unsigned long flags; -- int ret = 0; -- -- if ((burst_size != 8) && (burst_size != 16)) { -- dev_err(ipu->dev, "Illegal burst length for IC\n"); -- return -EINVAL; -- } -- -- width--; -- height--; -- -- if (temp_rot & 0x2) /* Need horizontal flip */ -- need_hor_flip = true; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -- ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -- ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -- -- switch (channel->num) { -- case IPUV3_CHANNEL_IC_PP_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -- break; -- case IPUV3_CHANNEL_MEM_ROT_PP: -- ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -- break; -- case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_ENC: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_IC_PRP_VF_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_VF: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -- break; -- case IPUV3_CHANNEL_VDI_MEM_IC_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -- break; -- default: -- goto unlock; -- } -- -- ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -- ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -- ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -- -- if (ipu_rot_mode_is_irt(rot)) -- ic->rotation = true; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -- --static void ipu_irt_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (!priv->irt_use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -- -- priv->irt_use_count++; --} -- --static void ipu_irt_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (priv->irt_use_count) { -- if (!--priv->irt_use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -- } --} -- --int ipu_ic_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -- -- priv->use_count++; -- -- if (ic->rotation) -- ipu_irt_enable(ic); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_enable); -- --int ipu_ic_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- if (ic->rotation) -- ipu_irt_disable(ic); -- -- ic->rotation = ic->graphics = false; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_disable); -- --struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) --{ -- struct ipu_ic_priv *priv = ipu->ic_priv; -- unsigned long flags; -- struct ipu_ic *ic, *ret; -- -- if (task >= IC_NUM_TASKS) -- return ERR_PTR(-EINVAL); -- -- ic = &priv->task[task]; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (ic->in_use) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- ic->in_use = true; -- ret = ic; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_get); -- --void ipu_ic_put(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- ic->in_use = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_put); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base) --{ -- struct ipu_ic_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->ic_priv = priv; -- -- spin_lock_init(&priv->lock); -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -- if (!priv->tpmem_base) -- return -ENOMEM; -- -- dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -- -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- priv->task[i].task = i; -- priv->task[i].priv = priv; -- priv->task[i].reg = &ic_task_reg[i]; -- priv->task[i].bit = &ic_task_bit[i]; -- } -- -- return 0; --} -- --void ipu_ic_exit(struct ipu_soc *ipu) --{ --} -- --void ipu_ic_dump(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- -- dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -- ipu_ic_read(ic, IC_CONF)); -- dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_ENC_RSC)); -- dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_VF_RSC)); -- dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PP_RSC)); -- dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_1)); -- dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_1)); -- dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_3)); -- dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_4)); --} --EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- a/drivers/gpu/ipu-v3/ipu-image-convert.c -+++ /dev/null -@@ -1,2475 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * -- * Queued image conversion support, with tiling and rotation. -- */ -- --#include <linux/interrupt.h> --#include <linux/dma-mapping.h> --#include <video/imx-ipu-image-convert.h> --#include "ipu-prv.h" -- --/* -- * The IC Resizer has a restriction that the output frame from the -- * resizer must be 1024 or less in both width (pixels) and height -- * (lines). -- * -- * The image converter attempts to split up a conversion when -- * the desired output (converted) frame resolution exceeds the -- * IC resizer limit of 1024 in either dimension. -- * -- * If either dimension of the output frame exceeds the limit, the -- * dimension is split into 1, 2, or 4 equal stripes, for a maximum -- * of 4*4 or 16 tiles. A conversion is then carried out for each -- * tile (but taking care to pass the full frame stride length to -- * the DMA channel's parameter memory!). IDMA double-buffering is used -- * to convert each tile back-to-back when possible (see note below -- * when double_buffering boolean is set). -- * -- * Note that the input frame must be split up into the same number -- * of tiles as the output frame: -- * -- * +---------+-----+ -- * +-----+---+ | A | B | -- * | A | B | | | | -- * +-----+---+ --> +---------+-----+ -- * | C | D | | C | D | -- * +-----+---+ | | | -- * +---------+-----+ -- * -- * Clockwise 90° rotations are handled by first rescaling into a -- * reusable temporary tile buffer and then rotating with the 8x8 -- * block rotator, writing to the correct destination: -- * -- * +-----+-----+ -- * | | | -- * +-----+---+ +---------+ | C | A | -- * | A | B | | A,B, | | | | | -- * +-----+---+ --> | C,D | | --> | | | -- * | C | D | +---------+ +-----+-----+ -- * +-----+---+ | D | B | -- * | | | -- * +-----+-----+ -- * -- * If the 8x8 block rotator is used, horizontal or vertical flipping -- * is done during the rotation step, otherwise flipping is done -- * during the scaling step. -- * With rotation or flipping, tile order changes between input and -- * output image. Tiles are numbered row major from top left to bottom -- * right for both input and output image. -- */ -- --#define MAX_STRIPES_W 4 --#define MAX_STRIPES_H 4 --#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -- --#define MIN_W 16 --#define MIN_H 8 --#define MAX_W 4096 --#define MAX_H 4096 -- --enum ipu_image_convert_type { -- IMAGE_CONVERT_IN = 0, -- IMAGE_CONVERT_OUT, --}; -- --struct ipu_image_convert_dma_buf { -- void *virt; -- dma_addr_t phys; -- unsigned long len; --}; -- --struct ipu_image_convert_dma_chan { -- int in; -- int out; -- int rot_in; -- int rot_out; -- int vdi_in_p; -- int vdi_in; -- int vdi_in_n; --}; -- --/* dimensions of one tile */ --struct ipu_image_tile { -- u32 width; -- u32 height; -- u32 left; -- u32 top; -- /* size and strides are in bytes */ -- u32 size; -- u32 stride; -- u32 rot_stride; -- /* start Y or packed offset of this tile */ -- u32 offset; -- /* offset from start to tile in U plane, for planar formats */ -- u32 u_off; -- /* offset from start to tile in V plane, for planar formats */ -- u32 v_off; --}; -- --struct ipu_image_convert_image { -- struct ipu_image base; -- enum ipu_image_convert_type type; -- -- const struct ipu_image_pixfmt *fmt; -- unsigned int stride; -- -- /* # of rows (horizontal stripes) if dest height is > 1024 */ -- unsigned int num_rows; -- /* # of columns (vertical stripes) if dest width is > 1024 */ -- unsigned int num_cols; -- -- struct ipu_image_tile tile[MAX_TILES]; --}; -- --struct ipu_image_pixfmt { -- u32 fourcc; /* V4L2 fourcc */ -- int bpp; /* total bpp */ -- int uv_width_dec; /* decimation in width for U/V planes */ -- int uv_height_dec; /* decimation in height for U/V planes */ -- bool planar; /* planar format */ -- bool uv_swapped; /* U and V planes are swapped */ -- bool uv_packed; /* partial planar (U and V in same plane) */ --}; -- --struct ipu_image_convert_ctx; --struct ipu_image_convert_chan; --struct ipu_image_convert_priv; -- --struct ipu_image_convert_ctx { -- struct ipu_image_convert_chan *chan; -- -- ipu_image_convert_cb_t complete; -- void *complete_context; -- -- /* Source/destination image data and rotation mode */ -- struct ipu_image_convert_image in; -- struct ipu_image_convert_image out; -- struct ipu_ic_csc csc; -- enum ipu_rotate_mode rot_mode; -- u32 downsize_coeff_h; -- u32 downsize_coeff_v; -- u32 image_resize_coeff_h; -- u32 image_resize_coeff_v; -- u32 resize_coeffs_h[MAX_STRIPES_W]; -- u32 resize_coeffs_v[MAX_STRIPES_H]; -- -- /* intermediate buffer for rotation */ -- struct ipu_image_convert_dma_buf rot_intermediate[2]; -- -- /* current buffer number for double buffering */ -- int cur_buf_num; -- -- bool aborting; -- struct completion aborted; -- -- /* can we use double-buffering for this conversion operation? */ -- bool double_buffering; -- /* num_rows * num_cols */ -- unsigned int num_tiles; -- /* next tile to process */ -- unsigned int next_tile; -- /* where to place converted tile in dest image */ -- unsigned int out_tile_map[MAX_TILES]; -- -- struct list_head list; --}; -- --struct ipu_image_convert_chan { -- struct ipu_image_convert_priv *priv; -- -- enum ipu_ic_task ic_task; -- const struct ipu_image_convert_dma_chan *dma_ch; -- -- struct ipu_ic *ic; -- struct ipuv3_channel *in_chan; -- struct ipuv3_channel *out_chan; -- struct ipuv3_channel *rotation_in_chan; -- struct ipuv3_channel *rotation_out_chan; -- -- /* the IPU end-of-frame irqs */ -- int out_eof_irq; -- int rot_out_eof_irq; -- -- spinlock_t irqlock; -- -- /* list of convert contexts */ -- struct list_head ctx_list; -- /* queue of conversion runs */ -- struct list_head pending_q; -- /* queue of completed runs */ -- struct list_head done_q; -- -- /* the current conversion run */ -- struct ipu_image_convert_run *current_run; --}; -- --struct ipu_image_convert_priv { -- struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -- struct ipu_soc *ipu; --}; -- --static const struct ipu_image_convert_dma_chan --image_convert_dma_chan[IC_NUM_TASKS] = { -- [IC_TASK_VIEWFINDER] = { -- .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -- .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -- .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -- .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -- .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -- .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .in = IPUV3_CHANNEL_MEM_IC_PP, -- .out = IPUV3_CHANNEL_IC_PP_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -- .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -- }, --}; -- --static const struct ipu_image_pixfmt image_convert_formats[] = { -- { -- .fourcc = V4L2_PIX_FMT_RGB565, -- .bpp = 16, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XRGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XBGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGRX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_RGBX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_YUYV, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_UYVY, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- }, { -- .fourcc = V4L2_PIX_FMT_YVU420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_swapped = true, -- }, { -- .fourcc = V4L2_PIX_FMT_NV12, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_packed = true, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV422P, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_NV16, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- .uv_packed = true, -- }, --}; -- --static const struct ipu_image_pixfmt *get_format(u32 fourcc) --{ -- const struct ipu_image_pixfmt *ret = NULL; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -- if (image_convert_formats[i].fourcc == fourcc) { -- ret = &image_convert_formats[i]; -- break; -- } -- } -- -- return ret; --} -- --static void dump_format(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -- chan->ic_task, ctx, -- ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -- ic_image->base.pix.width, ic_image->base.pix.height, -- ic_image->num_cols, ic_image->num_rows, -- ic_image->fmt->fourcc & 0xff, -- (ic_image->fmt->fourcc >> 8) & 0xff, -- (ic_image->fmt->fourcc >> 16) & 0xff, -- (ic_image->fmt->fourcc >> 24) & 0xff); --} -- --int ipu_image_convert_enum_format(int index, u32 *fourcc) --{ -- const struct ipu_image_pixfmt *fmt; -- -- if (index >= (int)ARRAY_SIZE(image_convert_formats)) -- return -EINVAL; -- -- /* Format found */ -- fmt = &image_convert_formats[index]; -- *fourcc = fmt->fourcc; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -- --static void free_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf) --{ -- if (buf->virt) -- dma_free_coherent(priv->ipu->dev, -- buf->len, buf->virt, buf->phys); -- buf->virt = NULL; -- buf->phys = 0; --} -- --static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf, -- int size) --{ -- buf->len = PAGE_ALIGN(size); -- buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -- GFP_DMA | GFP_KERNEL); -- if (!buf->virt) { -- dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -- return -ENOMEM; -- } -- -- return 0; --} -- --static inline int num_stripes(int dim) --{ -- return (dim - 1) / 1024 + 1; --} -- --/* -- * Calculate downsizing coefficients, which are the same for all tiles, -- * and initial bilinear resizing coefficients, which are used to find the -- * best seam positions. -- * Also determine the number of tiles necessary to guarantee that no tile -- * is larger than 1024 pixels in either dimension at the output and between -- * IC downsizing and main processing sections. -- */ --static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -- struct ipu_image *in, -- struct ipu_image *out) --{ -- u32 downsized_width = in->rect.width; -- u32 downsized_height = in->rect.height; -- u32 downsize_coeff_v = 0; -- u32 downsize_coeff_h = 0; -- u32 resized_width = out->rect.width; -- u32 resized_height = out->rect.height; -- u32 resize_coeff_h; -- u32 resize_coeff_v; -- u32 cols; -- u32 rows; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- resized_width = out->rect.height; -- resized_height = out->rect.width; -- } -- -- /* Do not let invalid input lead to an endless loop below */ -- if (WARN_ON(resized_width == 0 || resized_height == 0)) -- return -EINVAL; -- -- while (downsized_width >= resized_width * 2) { -- downsized_width >>= 1; -- downsize_coeff_h++; -- } -- -- while (downsized_height >= resized_height * 2) { -- downsized_height >>= 1; -- downsize_coeff_v++; -- } -- -- /* -- * Calculate the bilinear resizing coefficients that could be used if -- * we were converting with a single tile. The bottom right output pixel -- * should sample as close as possible to the bottom right input pixel -- * out of the decimator, but not overshoot it: -- */ -- resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -- resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -- -- /* -- * Both the output of the IC downsizing section before being passed to -- * the IC main processing section and the final output of the IC main -- * processing section must be <= 1024 pixels in both dimensions. -- */ -- cols = num_stripes(max_t(u32, downsized_width, resized_width)); -- rows = num_stripes(max_t(u32, downsized_height, resized_height)); -- -- dev_dbg(ctx->chan->priv->ipu->dev, -- "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -- __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -- resize_coeff_v, cols, rows); -- -- if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -- resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -- return -EINVAL; -- -- ctx->downsize_coeff_h = downsize_coeff_h; -- ctx->downsize_coeff_v = downsize_coeff_v; -- ctx->image_resize_coeff_h = resize_coeff_h; -- ctx->image_resize_coeff_v = resize_coeff_v; -- ctx->in.num_cols = cols; -- ctx->in.num_rows = rows; -- -- return 0; --} -- --#define round_closest(x, y) round_down((x) + (y)/2, (y)) -- --/* -- * Find the best aligned seam position for the given column / row index. -- * Rotation and image offsets are out of scope. -- * -- * @index: column / row index, used to calculate valid interval -- * @in_edge: input right / bottom edge -- * @out_edge: output right / bottom edge -- * @in_align: input alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format -- * @out_align: output alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format or rotator -- * block size -- * @in_burst: horizontal input burst size in case of horizontal flip -- * @out_burst: horizontal output burst size or rotator block size -- * @downsize_coeff: downsizing section coefficient -- * @resize_coeff: main processing section resizing coefficient -- * @_in_seam: aligned input seam position return value -- * @_out_seam: aligned output seam position return value -- */ --static void find_best_seam(struct ipu_image_convert_ctx *ctx, -- unsigned int index, -- unsigned int in_edge, -- unsigned int out_edge, -- unsigned int in_align, -- unsigned int out_align, -- unsigned int in_burst, -- unsigned int out_burst, -- unsigned int downsize_coeff, -- unsigned int resize_coeff, -- u32 *_in_seam, -- u32 *_out_seam) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int out_pos; -- /* Input / output seam position candidates */ -- unsigned int out_seam = 0; -- unsigned int in_seam = 0; -- unsigned int min_diff = UINT_MAX; -- unsigned int out_start; -- unsigned int out_end; -- unsigned int in_start; -- unsigned int in_end; -- -- /* Start within 1024 pixels of the right / bottom edge */ -- out_start = max_t(int, index * out_align, out_edge - 1024); -- /* End before having to add more columns to the left / rows above */ -- out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -- -- /* -- * Limit input seam position to make sure that the downsized input tile -- * to the right or bottom does not exceed 1024 pixels. -- */ -- in_start = max_t(int, index * in_align, -- in_edge - (1024 << downsize_coeff)); -- in_end = min_t(unsigned int, in_edge, -- index * (1024 << downsize_coeff) + 1); -- -- /* -- * Output tiles must start at a multiple of 8 bytes horizontally and -- * possibly at an even line horizontally depending on the pixel format. -- * Only consider output aligned positions for the seam. -- */ -- out_start = round_up(out_start, out_align); -- for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -- unsigned int in_pos; -- unsigned int in_pos_aligned; -- unsigned int in_pos_rounded; -- unsigned int abs_diff; -- -- /* -- * Tiles in the right row / bottom column may not be allowed to -- * overshoot horizontally / vertically. out_burst may be the -- * actual DMA burst size, or the rotator block size. -- */ -- if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -- continue; -- -- /* -- * Input sample position, corresponding to out_pos, 19.13 fixed -- * point. -- */ -- in_pos = (out_pos * resize_coeff) << downsize_coeff; -- /* -- * The closest input sample position that we could actually -- * start the input tile at, 19.13 fixed point. -- */ -- in_pos_aligned = round_closest(in_pos, 8192U * in_align); -- /* Convert 19.13 fixed point to integer */ -- in_pos_rounded = in_pos_aligned / 8192U; -- -- if (in_pos_rounded < in_start) -- continue; -- if (in_pos_rounded >= in_end) -- break; -- -- if ((in_burst > 1) && -- (in_edge - in_pos_rounded) % in_burst) -- continue; -- -- if (in_pos < in_pos_aligned) -- abs_diff = in_pos_aligned - in_pos; -- else -- abs_diff = in_pos - in_pos_aligned; -- -- if (abs_diff < min_diff) { -- in_seam = in_pos_rounded; -- out_seam = out_pos; -- min_diff = abs_diff; -- } -- } -- -- *_out_seam = out_seam; -- *_in_seam = in_seam; -- -- dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -- __func__, out_seam, out_align, out_start, out_end, -- in_seam, in_align, in_start, in_end, min_diff / 8192, -- DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); --} -- --/* -- * Tile left edges are required to be aligned to multiples of 8 bytes -- * by the IDMAC. -- */ --static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) --{ -- if (fmt->planar) -- return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -- else -- return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; --} -- --/* -- * Tile top edge alignment is only limited by chroma subsampling. -- */ --static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) --{ -- return fmt->uv_height_dec > 1 ? 2 : 1; --} -- --static inline u32 tile_width_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN) { -- /* -- * The IC burst reads 8 pixels at a time. Reading beyond the -- * end of the line is usually acceptable. Those pixels are -- * ignored, unless the IC has to write the scaled line in -- * reverse. -- */ -- return (!ipu_rot_mode_is_irt(rot_mode) && -- (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -- } -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (ipu_rot_mode_is_irt(rot_mode) && -- fmt->planar && !fmt->uv_packed) ? -- 8 * fmt->uv_width_dec : 8; --} -- --static inline u32 tile_height_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -- return 2; -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; --} -- --/* -- * Fill in left position and width and for all tiles in an input column, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a row, and output tile top position and height are set. -- */ --static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -- unsigned int col, -- struct ipu_image_convert_image *in, -- unsigned int in_left, unsigned int in_width, -- struct ipu_image_convert_image *out, -- unsigned int out_left, unsigned int out_width) --{ -- unsigned int row, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (row = 0; row < in->num_rows; row++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->left = in_left; -- in_tile->width = in_width; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->top = out_left; -- out_tile->height = out_width; -- } else { -- out_tile->left = out_left; -- out_tile->width = out_width; -- } -- } --} -- --/* -- * Fill in top position and height and for all tiles in an input row, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a column, and output tile left position and width are set. -- */ --static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -- struct ipu_image_convert_image *in, -- unsigned int in_top, unsigned int in_height, -- struct ipu_image_convert_image *out, -- unsigned int out_top, unsigned int out_height) --{ -- unsigned int col, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (col = 0; col < in->num_cols; col++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->top = in_top; -- in_tile->height = in_height; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->left = out_top; -- out_tile->width = out_height; -- } else { -- out_tile->top = out_top; -- out_tile->height = out_height; -- } -- } --} -- --/* -- * Find the best horizontal and vertical seam positions to split into tiles. -- * Minimize the fractional part of the input sampling position for the -- * top / left pixels of each tile. -- */ --static void find_seams(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *in, -- struct ipu_image_convert_image *out) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int resized_width = out->base.rect.width; -- unsigned int resized_height = out->base.rect.height; -- unsigned int col; -- unsigned int row; -- unsigned int in_left_align = tile_left_align(in->fmt); -- unsigned int in_top_align = tile_top_align(in->fmt); -- unsigned int out_left_align = tile_left_align(out->fmt); -- unsigned int out_top_align = tile_top_align(out->fmt); -- unsigned int out_width_align = tile_width_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int out_height_align = tile_height_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int in_right = in->base.rect.width; -- unsigned int in_bottom = in->base.rect.height; -- unsigned int out_right = out->base.rect.width; -- unsigned int out_bottom = out->base.rect.height; -- unsigned int flipped_out_left; -- unsigned int flipped_out_top; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* Switch width/height and align top left to IRT block size */ -- resized_width = out->base.rect.height; -- resized_height = out->base.rect.width; -- out_left_align = out_height_align; -- out_top_align = out_width_align; -- out_width_align = out_left_align; -- out_height_align = out_top_align; -- out_right = out->base.rect.height; -- out_bottom = out->base.rect.width; -- } -- -- for (col = in->num_cols - 1; col > 0; col--) { -- bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- bool allow_out_overshoot = (col < in->num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- unsigned int in_left; -- unsigned int out_left; -- -- /* -- * Align input width to burst length if the scaling step flips -- * horizontally. -- */ -- -- find_best_seam(ctx, col, -- in_right, out_right, -- in_left_align, out_left_align, -- allow_in_overshoot ? 1 : 8 /* burst length */, -- allow_out_overshoot ? 1 : out_width_align, -- ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -- &in_left, &out_left); -- -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- flipped_out_left = resized_width - out_right; -- else -- flipped_out_left = out_left; -- -- fill_tile_column(ctx, col, in, in_left, in_right - in_left, -- out, flipped_out_left, out_right - out_left); -- -- dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -- in_left, in_right - in_left, -- flipped_out_left, out_right - out_left); -- -- in_right = in_left; -- out_right = out_left; -- } -- -- flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -- resized_width - out_right : 0; -- -- fill_tile_column(ctx, 0, in, 0, in_right, -- out, flipped_out_left, out_right); -- -- dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -- in_right, flipped_out_left, out_right); -- -- for (row = in->num_rows - 1; row > 0; row--) { -- bool allow_overshoot = row < in->num_rows - 1; -- unsigned int in_top; -- unsigned int out_top; -- -- find_best_seam(ctx, row, -- in_bottom, out_bottom, -- in_top_align, out_top_align, -- 1, allow_overshoot ? 1 : out_height_align, -- ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -- &in_top, &out_top); -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = out_top; -- -- fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -- out, flipped_out_top, out_bottom - out_top); -- -- dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -- in_top, in_bottom - in_top, -- flipped_out_top, out_bottom - out_top); -- -- in_bottom = in_top; -- out_bottom = out_top; -- } -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = 0; -- -- fill_tile_row(ctx, 0, in, 0, in_bottom, -- out, flipped_out_top, out_bottom); -- -- dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -- in_bottom, flipped_out_top, out_bottom); --} -- --static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned int max_width = 1024; -- unsigned int max_height = 1024; -- unsigned int i; -- -- if (image->type == IMAGE_CONVERT_IN) { -- /* Up to 4096x4096 input tile size */ -- max_width <<= ctx->downsize_coeff_h; -- max_height <<= ctx->downsize_coeff_v; -- } -- -- for (i = 0; i < ctx->num_tiles; i++) { -- struct ipu_image_tile *tile; -- const unsigned int row = i / image->num_cols; -- const unsigned int col = i % image->num_cols; -- -- if (image->type == IMAGE_CONVERT_OUT) -- tile = &image->tile[ctx->out_tile_map[i]]; -- else -- tile = &image->tile[i]; -- -- tile->size = ((tile->height * image->fmt->bpp) >> 3) * -- tile->width; -- -- if (image->fmt->planar) { -- tile->stride = tile->width; -- tile->rot_stride = tile->height; -- } else { -- tile->stride = -- (image->fmt->bpp * tile->width) >> 3; -- tile->rot_stride = -- (image->fmt->bpp * tile->height) >> 3; -- } -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -- row, col, -- tile->width, tile->height, tile->left, tile->top); -- -- if (!tile->width || tile->width > max_width || -- !tile->height || tile->height > max_height) { -- dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -- image->type == IMAGE_CONVERT_IN ? "input" : -- "output", tile->width, tile->height); -- return -EINVAL; -- } -- } -- -- return 0; --} -- --/* -- * Use the rotation transformation to find the tile coordinates -- * (row, col) of a tile in the destination frame that corresponds -- * to the given tile coordinates of a source frame. The destination -- * coordinate is then converted to a tile index. -- */ --static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -- int src_row, int src_col) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- int dst_row, dst_col; -- -- /* with no rotation it's a 1:1 mapping */ -- if (ctx->rot_mode == IPU_ROTATE_NONE) -- return src_row * s_image->num_cols + src_col; -- -- /* -- * before doing the transform, first we have to translate -- * source row,col for an origin in the center of s_image -- */ -- src_row = src_row * 2 - (s_image->num_rows - 1); -- src_col = src_col * 2 - (s_image->num_cols - 1); -- -- /* do the rotation transform */ -- if (ctx->rot_mode & IPU_ROT_BIT_90) { -- dst_col = -src_row; -- dst_row = src_col; -- } else { -- dst_col = src_col; -- dst_row = src_row; -- } -- -- /* apply flip */ -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- dst_col = -dst_col; -- if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -- dst_row = -dst_row; -- -- dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -- chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -- -- /* -- * finally translate dest row,col using an origin in upper -- * left of d_image -- */ -- dst_row += d_image->num_rows - 1; -- dst_col += d_image->num_cols - 1; -- dst_row /= 2; -- dst_col /= 2; -- -- return dst_row * d_image->num_cols + dst_col; --} -- --/* -- * Fill the out_tile_map[] with transformed destination tile indeces. -- */ --static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_image *s_image = &ctx->in; -- unsigned int row, col, tile = 0; -- -- for (row = 0; row < s_image->num_rows; row++) { -- for (col = 0; col < s_image->num_cols; col++) { -- ctx->out_tile_map[tile] = -- transform_tile_index(ctx, row, col); -- tile++; -- } -- } --} -- --static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 H, top, y_stride, uv_stride; -- u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -- u32 y_row_off, y_col_off, y_off; -- u32 y_size, uv_size; -- -- /* setup some convenience vars */ -- H = image->base.pix.height; -- -- y_stride = image->stride; -- uv_stride = y_stride / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_stride *= 2; -- -- y_size = H * y_stride; -- uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -- -- for (row = 0; row < image->num_rows; row++) { -- top = image->tile[tile].top; -- y_row_off = top * y_stride; -- uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -- -- for (col = 0; col < image->num_cols; col++) { -- y_col_off = image->tile[tile].left; -- uv_col_off = y_col_off / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_col_off *= 2; -- -- y_off = y_row_off + y_col_off; -- uv_off = uv_row_off + uv_col_off; -- -- u_off = y_size - y_off + uv_off; -- v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -- if (fmt->uv_swapped) { -- tmp = u_off; -- u_off = v_off; -- v_off = tmp; -- } -- -- image->tile[tile].offset = y_off; -- image->tile[tile].u_off = u_off; -- image->tile[tile++].v_off = v_off; -- -- if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "y_off %08x, u_off %08x, v_off %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- y_off, u_off, v_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 bpp, stride, offset; -- u32 row_off, col_off; -- -- /* setup some convenience vars */ -- stride = image->stride; -- bpp = fmt->bpp; -- -- for (row = 0; row < image->num_rows; row++) { -- row_off = image->tile[tile].top * stride; -- -- for (col = 0; col < image->num_cols; col++) { -- col_off = (image->tile[tile].left * bpp) >> 3; -- -- offset = row_off + col_off; -- -- image->tile[tile].offset = offset; -- image->tile[tile].u_off = 0; -- image->tile[tile++].v_off = 0; -- -- if (offset & 0x7) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "phys %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- row_off + col_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- if (image->fmt->planar) -- return calc_tile_offsets_planar(ctx, image); -- -- return calc_tile_offsets_packed(ctx, image); --} -- --/* -- * Calculate the resizing ratio for the IC main processing section given input -- * size, fixed downsizing coefficient, and output size. -- * Either round to closest for the next tile's first pixel to minimize seams -- * and distortion (for all but right column / bottom row), or round down to -- * avoid sampling beyond the edges of the input image for this tile's last -- * pixel. -- * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -- */ --static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -- u32 output_size, bool allow_overshoot) --{ -- u32 downsized = input_size >> downsize_coeff; -- -- if (allow_overshoot) -- return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -- else -- return 8192 * (downsized - 1) / (output_size - 1); --} -- --/* -- * Slightly modify resize coefficients per tile to hide the bilinear -- * interpolator reset at tile borders, shifting the right / bottom edge -- * by up to a half input pixel. This removes noticeable seams between -- * tiles at higher upscaling factors. -- */ --static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_tile *in_tile, *out_tile; -- unsigned int col, row, tile_idx; -- unsigned int last_output; -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- bool closest = (col < ctx->in.num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- u32 resized_width; -- u32 resize_coeff_h; -- u32 in_width; -- -- tile_idx = col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_width = out_tile->height; -- else -- resized_width = out_tile->width; -- -- resize_coeff_h = calc_resize_coeff(in_tile->width, -- ctx->downsize_coeff_h, -- resized_width, closest); -- -- dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -- __func__, col, resize_coeff_h); -- -- /* -- * With the horizontal scaling factor known, round up resized -- * width (output width or height) to burst size. -- */ -- resized_width = round_up(resized_width, 8); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized width and scaling coefficients. Round up to -- * burst size. -- */ -- last_output = resized_width - 1; -- if (closest && ((last_output * resize_coeff_h) % 8192)) -- last_output++; -- in_width = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -- << ctx->downsize_coeff_h, 8); -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->height = resized_width; -- else -- out_tile->width = resized_width; -- -- in_tile->width = in_width; -- } -- -- ctx->resize_coeffs_h[col] = resize_coeff_h; -- } -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- bool closest = (row < ctx->in.num_rows - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -- u32 resized_height; -- u32 resize_coeff_v; -- u32 in_height; -- -- tile_idx = row * ctx->in.num_cols; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_height = out_tile->width; -- else -- resized_height = out_tile->height; -- -- resize_coeff_v = calc_resize_coeff(in_tile->height, -- ctx->downsize_coeff_v, -- resized_height, closest); -- -- dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -- __func__, row, resize_coeff_v); -- -- /* -- * With the vertical scaling factor known, round up resized -- * height (output width or height) to IDMAC limitations. -- */ -- resized_height = round_up(resized_height, 2); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized height and scaling coefficients. Align to -- * IDMAC restrictions. -- */ -- last_output = resized_height - 1; -- if (closest && ((last_output * resize_coeff_v) % 8192)) -- last_output++; -- in_height = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -- << ctx->downsize_coeff_v, 2); -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->width = resized_height; -- else -- out_tile->height = resized_height; -- -- in_tile->height = in_height; -- } -- -- ctx->resize_coeffs_v[row] = resize_coeff_v; -- } --} -- --/* -- * return the number of runs in given queue (pending_q or done_q) -- * for this context. hold irqlock when calling. -- */ --static int get_run_count(struct ipu_image_convert_ctx *ctx, -- struct list_head *q) --{ -- struct ipu_image_convert_run *run; -- int count = 0; -- -- lockdep_assert_held(&ctx->chan->irqlock); -- -- list_for_each_entry(run, q, list) { -- if (run->ctx == ctx) -- count++; -- } -- -- return count; --} -- --static void convert_stop(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -- __func__, chan->ic_task, ctx, run); -- -- /* disable IC tasks and the channels */ -- ipu_ic_task_disable(chan->ic); -- ipu_idmac_disable_channel(chan->in_chan); -- ipu_idmac_disable_channel(chan->out_chan); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_disable_channel(chan->rotation_in_chan); -- ipu_idmac_disable_channel(chan->rotation_out_chan); -- ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -- } -- -- ipu_ic_disable(chan->ic); --} -- --static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -- struct ipuv3_channel *channel, -- struct ipu_image_convert_image *image, -- enum ipu_rotate_mode rot_mode, -- bool rot_swap_width_height, -- unsigned int tile) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- unsigned int burst_size; -- u32 width, height, stride; -- dma_addr_t addr0, addr1 = 0; -- struct ipu_image tile_image; -- unsigned int tile_idx[2]; -- -- if (image->type == IMAGE_CONVERT_OUT) { -- tile_idx[0] = ctx->out_tile_map[tile]; -- tile_idx[1] = ctx->out_tile_map[1]; -- } else { -- tile_idx[0] = tile; -- tile_idx[1] = 1; -- } -- -- if (rot_swap_width_height) { -- width = image->tile[tile_idx[0]].height; -- height = image->tile[tile_idx[0]].width; -- stride = image->tile[tile_idx[0]].rot_stride; -- addr0 = ctx->rot_intermediate[0].phys; -- if (ctx->double_buffering) -- addr1 = ctx->rot_intermediate[1].phys; -- } else { -- width = image->tile[tile_idx[0]].width; -- height = image->tile[tile_idx[0]].height; -- stride = image->stride; -- addr0 = image->base.phys0 + -- image->tile[tile_idx[0]].offset; -- if (ctx->double_buffering) -- addr1 = image->base.phys0 + -- image->tile[tile_idx[1]].offset; -- } -- -- ipu_cpmem_zero(channel); -- -- memset(&tile_image, 0, sizeof(tile_image)); -- tile_image.pix.width = tile_image.rect.width = width; -- tile_image.pix.height = tile_image.rect.height = height; -- tile_image.pix.bytesperline = stride; -- tile_image.pix.pixelformat = image->fmt->fourcc; -- tile_image.phys0 = addr0; -- tile_image.phys1 = addr1; -- if (image->fmt->planar && !rot_swap_width_height) { -- tile_image.u_offset = image->tile[tile_idx[0]].u_off; -- tile_image.v_offset = image->tile[tile_idx[0]].v_off; -- } -- -- ipu_cpmem_set_image(channel, &tile_image); -- -- if (rot_mode) -- ipu_cpmem_set_rotation(channel, rot_mode); -- -- /* -- * Skip writing U and V components to odd rows in the output -- * channels for planar 4:2:0. -- */ -- if ((channel == chan->out_chan || -- channel == chan->rotation_out_chan) && -- image->fmt->planar && image->fmt->uv_height_dec == 2) -- ipu_cpmem_skip_odd_chroma_rows(channel); -- -- if (channel == chan->rotation_in_chan || -- channel == chan->rotation_out_chan) { -- burst_size = 8; -- ipu_cpmem_set_block_mode(channel); -- } else -- burst_size = (width % 16) ? 8 : 16; -- -- ipu_cpmem_set_burstsize(channel, burst_size); -- -- ipu_ic_task_idma_init(chan->ic, channel, width, height, -- burst_size, rot_mode); -- -- /* -- * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -- * only do this when there is no PRG present. -- */ -- if (!channel->ipu->prg_priv) -- ipu_cpmem_set_axi_id(channel, 1); -- -- ipu_idmac_set_double_buffer(channel, ctx->double_buffering); --} -- --static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- unsigned int dst_tile = ctx->out_tile_map[tile]; -- unsigned int dest_width, dest_height; -- unsigned int col, row; -- u32 rsc; -- int ret; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -- __func__, chan->ic_task, ctx, run, tile, dst_tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* swap width/height for resizer */ -- dest_width = d_image->tile[dst_tile].height; -- dest_height = d_image->tile[dst_tile].width; -- } else { -- dest_width = d_image->tile[dst_tile].width; -- dest_height = d_image->tile[dst_tile].height; -- } -- -- row = tile / s_image->num_cols; -- col = tile % s_image->num_cols; -- -- rsc = (ctx->downsize_coeff_v << 30) | -- (ctx->resize_coeffs_v[row] << 16) | -- (ctx->downsize_coeff_h << 14) | -- (ctx->resize_coeffs_h[col]); -- -- dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -- __func__, s_image->tile[tile].width, -- s_image->tile[tile].height, dest_width, dest_height, rsc); -- -- /* setup the IC resizer and CSC */ -- ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -- s_image->tile[tile].width, -- s_image->tile[tile].height, -- dest_width, -- dest_height, -- rsc); -- if (ret) { -- dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -- return ret; -- } -- -- /* init the source MEM-->IC PP IDMAC channel */ -- init_idmac_channel(ctx, chan->in_chan, s_image, -- IPU_ROTATE_NONE, false, tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* init the IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- IPU_ROTATE_NONE, true, tile); -- -- /* init the MEM-->IC PP ROT IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -- ctx->rot_mode, true, tile); -- -- /* init the destination IC PP ROT-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -- IPU_ROTATE_NONE, false, tile); -- -- /* now link IC PP-->MEM to MEM-->IC PP ROT */ -- ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -- } else { -- /* init the destination IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- ctx->rot_mode, false, tile); -- } -- -- /* enable the IC */ -- ipu_ic_enable(chan->ic); -- -- /* set buffers ready */ -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(chan->out_chan, 0); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -- if (ctx->double_buffering) { -- ipu_idmac_select_buffer(chan->in_chan, 1); -- ipu_idmac_select_buffer(chan->out_chan, 1); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -- } -- -- /* enable the channels! */ -- ipu_idmac_enable_channel(chan->in_chan); -- ipu_idmac_enable_channel(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_enable_channel(chan->rotation_in_chan); -- ipu_idmac_enable_channel(chan->rotation_out_chan); -- } -- -- ipu_ic_task_enable(chan->ic); -- -- ipu_cpmem_dump(chan->in_chan); -- ipu_cpmem_dump(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_cpmem_dump(chan->rotation_in_chan); -- ipu_cpmem_dump(chan->rotation_out_chan); -- } -- -- ipu_dump(priv->ipu); -- -- return 0; --} -- --/* hold irqlock when calling */ --static int do_run(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- -- lockdep_assert_held(&chan->irqlock); -- -- ctx->in.base.phys0 = run->in_phys; -- ctx->out.base.phys0 = run->out_phys; -- -- ctx->cur_buf_num = 0; -- ctx->next_tile = 1; -- -- /* remove run from pending_q and set as current */ -- list_del(&run->list); -- chan->current_run = run; -- -- return convert_start(run, 0); --} -- --/* hold irqlock when calling */ --static void run_next(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *tmp; -- int ret; -- -- lockdep_assert_held(&chan->irqlock); -- -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- /* skip contexts that are aborting */ -- if (run->ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: skipping aborting ctx %p run %p\n", -- __func__, chan->ic_task, run->ctx, run); -- continue; -- } -- -- ret = do_run(run); -- if (!ret) -- break; -- -- /* -- * something went wrong with start, add the run -- * to done q and continue to the next run in the -- * pending q. -- */ -- run->status = ret; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- } --} -- --static void empty_done_q(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- while (!list_empty(&chan->done_q)) { -- run = list_entry(chan->done_q.next, -- struct ipu_image_convert_run, -- list); -- -- list_del(&run->list); -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: completing ctx %p run %p with %d\n", -- __func__, chan->ic_task, run->ctx, run, run->status); -- -- /* call the completion callback and free the run */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- run->ctx->complete(run, run->ctx->complete_context); -- spin_lock_irqsave(&chan->irqlock, flags); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); --} -- --/* -- * the bottom half thread clears out the done_q, calling the -- * completion handler for each. -- */ --static irqreturn_t do_bh(int irq, void *dev_id) --{ -- struct ipu_image_convert_chan *chan = dev_id; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -- chan->ic_task); -- -- empty_done_q(chan); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* -- * the done_q is cleared out, signal any contexts -- * that are aborting that abort can complete. -- */ -- list_for_each_entry(ctx, &chan->ctx_list, list) { -- if (ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: signaling abort for ctx %p\n", -- __func__, chan->ic_task, ctx); -- complete_all(&ctx->aborted); -- } -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -- chan->ic_task); -- -- return IRQ_HANDLED; --} -- --static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) --{ -- unsigned int cur_tile = ctx->next_tile - 1; -- unsigned int next_tile = ctx->next_tile; -- -- if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -- ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -- ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -- ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -- ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -- ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -- ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -- ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -- return true; -- -- return false; --} -- --/* hold irqlock when calling */ --static irqreturn_t do_irq(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_tile *src_tile, *dst_tile; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- struct ipuv3_channel *outch; -- unsigned int dst_idx; -- -- lockdep_assert_held(&chan->irqlock); -- -- outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -- chan->rotation_out_chan : chan->out_chan; -- -- /* -- * It is difficult to stop the channel DMA before the channels -- * enter the paused state. Without double-buffering the channels -- * are always in a paused state when the EOF irq occurs, so it -- * is safe to stop the channels now. For double-buffering we -- * just ignore the abort until the operation completes, when it -- * is safe to shut down. -- */ -- if (ctx->aborting && !ctx->double_buffering) { -- convert_stop(run); -- run->status = -EIO; -- goto done; -- } -- -- if (ctx->next_tile == ctx->num_tiles) { -- /* -- * the conversion is complete -- */ -- convert_stop(run); -- run->status = 0; -- goto done; -- } -- -- /* -- * not done, place the next tile buffers. -- */ -- if (!ctx->double_buffering) { -- if (ic_settings_changed(ctx)) { -- convert_stop(run); -- convert_start(run, ctx->next_tile); -- } else { -- src_tile = &s_image->tile[ctx->next_tile]; -- dst_idx = ctx->out_tile_map[ctx->next_tile]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, 0, -- s_image->base.phys0 + -- src_tile->offset); -- ipu_cpmem_set_buffer(outch, 0, -- d_image->base.phys0 + -- dst_tile->offset); -- if (s_image->fmt->planar) -- ipu_cpmem_set_uv_offset(chan->in_chan, -- src_tile->u_off, -- src_tile->v_off); -- if (d_image->fmt->planar) -- ipu_cpmem_set_uv_offset(outch, -- dst_tile->u_off, -- dst_tile->v_off); -- -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(outch, 0); -- } -- } else if (ctx->next_tile < ctx->num_tiles - 1) { -- -- src_tile = &s_image->tile[ctx->next_tile + 1]; -- dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -- s_image->base.phys0 + src_tile->offset); -- ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -- d_image->base.phys0 + dst_tile->offset); -- -- ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -- ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -- -- ctx->cur_buf_num ^= 1; -- } -- -- ctx->next_tile++; -- return IRQ_HANDLED; --done: -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- return IRQ_WAKE_THREAD; --} -- --static irqreturn_t norotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this is a rotation operation, just ignore */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --static irqreturn_t rotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this was NOT a rotation operation, shouldn't happen */ -- dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --/* -- * try to force the completion of runs for this ctx. Called when -- * abort wait times out in ipu_image_convert_abort(). -- */ --static void force_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- run = chan->current_run; -- if (run && run->ctx == ctx) { -- convert_stop(run); -- run->status = -EIO; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- empty_done_q(chan); --} -- --static void release_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- if (chan->out_eof_irq >= 0) -- free_irq(chan->out_eof_irq, chan); -- if (chan->rot_out_eof_irq >= 0) -- free_irq(chan->rot_out_eof_irq, chan); -- -- if (!IS_ERR_OR_NULL(chan->in_chan)) -- ipu_idmac_put(chan->in_chan); -- if (!IS_ERR_OR_NULL(chan->out_chan)) -- ipu_idmac_put(chan->out_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -- ipu_idmac_put(chan->rotation_in_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -- ipu_idmac_put(chan->rotation_out_chan); -- if (!IS_ERR_OR_NULL(chan->ic)) -- ipu_ic_put(chan->ic); -- -- chan->in_chan = chan->out_chan = chan->rotation_in_chan = -- chan->rotation_out_chan = NULL; -- chan->out_eof_irq = chan->rot_out_eof_irq = -1; --} -- --static int get_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -- struct ipu_image_convert_priv *priv = chan->priv; -- int ret; -- -- /* get IC */ -- chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -- if (IS_ERR(chan->ic)) { -- dev_err(priv->ipu->dev, "could not acquire IC\n"); -- ret = PTR_ERR(chan->ic); -- goto err; -- } -- -- /* get IDMAC channels */ -- chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -- chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -- if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -- dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -- chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -- if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -- dev_err(priv->ipu->dev, -- "could not acquire idmac rotation channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- /* acquire the EOF interrupts */ -- chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->out_eof_irq); -- chan->out_eof_irq = -1; -- goto err; -- } -- -- chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->rotation_out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->rot_out_eof_irq); -- chan->rot_out_eof_irq = -1; -- goto err; -- } -- -- return 0; --err: -- release_ipu_resources(chan); -- return ret; --} -- --static int fill_image(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image, -- struct ipu_image *image, -- enum ipu_image_convert_type type) --{ -- struct ipu_image_convert_priv *priv = ctx->chan->priv; -- -- ic_image->base = *image; -- ic_image->type = type; -- -- ic_image->fmt = get_format(image->pix.pixelformat); -- if (!ic_image->fmt) { -- dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -- type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -- return -EINVAL; -- } -- -- if (ic_image->fmt->planar) -- ic_image->stride = ic_image->base.pix.width; -- else -- ic_image->stride = ic_image->base.pix.bytesperline; -- -- return 0; --} -- --/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ --static unsigned int clamp_align(unsigned int x, unsigned int min, -- unsigned int max, unsigned int align) --{ -- /* Bits that must be zero to be aligned */ -- unsigned int mask = ~((1 << align) - 1); -- -- /* Clamp to aligned min and max */ -- x = clamp(x, (min + ~mask) & mask, max & mask); -- -- /* Round to nearest aligned value */ -- if (align) -- x = (x + (1 << (align - 1))) & mask; -- -- return x; --} -- --/* Adjusts input/output images to IPU restrictions */ --void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- const struct ipu_image_pixfmt *infmt, *outfmt; -- u32 w_align_out, h_align_out; -- u32 w_align_in, h_align_in; -- -- infmt = get_format(in->pix.pixelformat); -- outfmt = get_format(out->pix.pixelformat); -- -- /* set some default pixel formats if needed */ -- if (!infmt) { -- in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- infmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- if (!outfmt) { -- out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- outfmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- -- /* image converter does not handle fields */ -- in->pix.field = out->pix.field = V4L2_FIELD_NONE; -- -- /* resizer cannot downsize more than 4:1 */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.width / 4); -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.height / 4); -- } else { -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.width / 4); -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.height / 4); -- } -- -- /* align input width/height */ -- w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -- w_align_in); -- in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -- h_align_in); -- -- /* align output width/height */ -- w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -- w_align_out); -- out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -- h_align_out); -- -- /* set input/output strides and image sizes */ -- in->pix.bytesperline = infmt->planar ? -- clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -- w_align_in) : -- clamp_align((in->pix.width * infmt->bpp) >> 3, -- ((2 << w_align_in) * infmt->bpp) >> 3, -- (MAX_W * infmt->bpp) >> 3, -- w_align_in); -- in->pix.sizeimage = infmt->planar ? -- (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -- in->pix.height * in->pix.bytesperline; -- out->pix.bytesperline = outfmt->planar ? out->pix.width : -- (out->pix.width * outfmt->bpp) >> 3; -- out->pix.sizeimage = outfmt->planar ? -- (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -- out->pix.height * out->pix.bytesperline; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -- --/* -- * this is used by ipu_image_convert_prepare() to verify set input and -- * output images are valid before starting the conversion. Clients can -- * also call it before calling ipu_image_convert_prepare(). -- */ --int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image testin, testout; -- -- testin = *in; -- testout = *out; -- -- ipu_image_convert_adjust(&testin, &testout, rot_mode); -- -- if (testin.pix.width != in->pix.width || -- testin.pix.height != in->pix.height || -- testout.pix.width != out->pix.width || -- testout.pix.height != out->pix.height) -- return -EINVAL; -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -- --/* -- * Call ipu_image_convert_prepare() to prepare for the conversion of -- * given images and rotation mode. Returns a new conversion context. -- */ --struct ipu_image_convert_ctx * --ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -- struct ipu_image_convert_image *s_image, *d_image; -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- unsigned int i; -- bool get_res; -- int ret; -- -- if (!in || !out || !complete || -- (ic_task != IC_TASK_VIEWFINDER && -- ic_task != IC_TASK_POST_PROCESSOR)) -- return ERR_PTR(-EINVAL); -- -- /* verify the in/out images before continuing */ -- ret = ipu_image_convert_verify(in, out, rot_mode); -- if (ret) { -- dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -- __func__); -- return ERR_PTR(ret); -- } -- -- chan = &priv->chan[ic_task]; -- -- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -- if (!ctx) -- return ERR_PTR(-ENOMEM); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- ctx->chan = chan; -- init_completion(&ctx->aborted); -- -- ctx->rot_mode = rot_mode; -- -- /* Sets ctx->in.num_rows/cols as well */ -- ret = calc_image_resize_coefficients(ctx, in, out); -- if (ret) -- goto out_free; -- -- s_image = &ctx->in; -- d_image = &ctx->out; -- -- /* set tiling and rotation */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- d_image->num_rows = s_image->num_cols; -- d_image->num_cols = s_image->num_rows; -- } else { -- d_image->num_rows = s_image->num_rows; -- d_image->num_cols = s_image->num_cols; -- } -- -- ctx->num_tiles = d_image->num_cols * d_image->num_rows; -- -- ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -- if (ret) -- goto out_free; -- ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -- if (ret) -- goto out_free; -- -- calc_out_tile_map(ctx); -- -- find_seams(ctx, s_image, d_image); -- -- ret = calc_tile_dimensions(ctx, s_image); -- if (ret) -- goto out_free; -- -- ret = calc_tile_offsets(ctx, s_image); -- if (ret) -- goto out_free; -- -- calc_tile_dimensions(ctx, d_image); -- ret = calc_tile_offsets(ctx, d_image); -- if (ret) -- goto out_free; -- -- calc_tile_resize_coefficients(ctx); -- -- ret = ipu_ic_calc_csc(&ctx->csc, -- s_image->base.pix.ycbcr_enc, -- s_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -- d_image->base.pix.ycbcr_enc, -- d_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -- if (ret) -- goto out_free; -- -- dump_format(ctx, s_image); -- dump_format(ctx, d_image); -- -- ctx->complete = complete; -- ctx->complete_context = complete_context; -- -- /* -- * Can we use double-buffering for this operation? If there is -- * only one tile (the whole image can be converted in a single -- * operation) there's no point in using double-buffering. Also, -- * the IPU's IDMAC channels allow only a single U and V plane -- * offset shared between both buffers, but these offsets change -- * for every tile, and therefore would have to be updated for -- * each buffer which is not possible. So double-buffering is -- * impossible when either the source or destination images are -- * a planar format (YUV420, YUV422P, etc.). Further, differently -- * sized tiles or different resizing coefficients per tile -- * prevent double-buffering as well. -- */ -- ctx->double_buffering = (ctx->num_tiles > 1 && -- !s_image->fmt->planar && -- !d_image->fmt->planar); -- for (i = 1; i < ctx->num_tiles; i++) { -- if (ctx->in.tile[i].width != ctx->in.tile[0].width || -- ctx->in.tile[i].height != ctx->in.tile[0].height || -- ctx->out.tile[i].width != ctx->out.tile[0].width || -- ctx->out.tile[i].height != ctx->out.tile[0].height) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_cols; i++) { -- if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_rows; i++) { -- if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- unsigned long intermediate_size = d_image->tile[0].size; -- -- for (i = 1; i < ctx->num_tiles; i++) { -- if (d_image->tile[i].size > intermediate_size) -- intermediate_size = d_image->tile[i].size; -- } -- -- ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -- intermediate_size); -- if (ret) -- goto out_free; -- if (ctx->double_buffering) { -- ret = alloc_dma_buf(priv, -- &ctx->rot_intermediate[1], -- intermediate_size); -- if (ret) -- goto out_free_dmabuf0; -- } -- } -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- get_res = list_empty(&chan->ctx_list); -- -- list_add_tail(&ctx->list, &chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (get_res) { -- ret = get_ipu_resources(chan); -- if (ret) -- goto out_free_dmabuf1; -- } -- -- return ctx; -- --out_free_dmabuf1: -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- spin_lock_irqsave(&chan->irqlock, flags); -- list_del(&ctx->list); -- spin_unlock_irqrestore(&chan->irqlock, flags); --out_free_dmabuf0: -- free_dma_buf(priv, &ctx->rot_intermediate[0]); --out_free: -- kfree(ctx); -- return ERR_PTR(ret); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -- --/* -- * Carry out a single image conversion run. Only the physaddr's of the input -- * and output image buffers are needed. The conversion context must have -- * been created previously with ipu_image_convert_prepare(). -- */ --int ipu_image_convert_queue(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_priv *priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- int ret = 0; -- -- if (!run || !run->ctx || !run->in_phys || !run->out_phys) -- return -EINVAL; -- -- ctx = run->ctx; -- chan = ctx->chan; -- priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -- chan->ic_task, ctx, run); -- -- INIT_LIST_HEAD(&run->list); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- if (ctx->aborting) { -- ret = -EIO; -- goto unlock; -- } -- -- list_add_tail(&run->list, &chan->pending_q); -- -- if (!chan->current_run) { -- ret = do_run(run); -- if (ret) -- chan->current_run = NULL; -- } --unlock: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -- --/* Abort any active or pending conversions for this context */ --static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *active_run, *tmp; -- unsigned long flags; -- int run_count, ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* move all remaining pending runs in this context to done_q */ -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- if (run->ctx != ctx) -- continue; -- run->status = -EIO; -- list_move_tail(&run->list, &chan->done_q); -- } -- -- run_count = get_run_count(ctx, &chan->done_q); -- active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -- chan->current_run : NULL; -- -- if (active_run) -- reinit_completion(&ctx->aborted); -- -- ctx->aborting = true; -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (!run_count && !active_run) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: no abort needed for ctx %p\n", -- __func__, chan->ic_task, ctx); -- return; -- } -- -- if (!active_run) { -- empty_done_q(chan); -- return; -- } -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: wait for completion: %d runs\n", -- __func__, chan->ic_task, run_count); -- -- ret = wait_for_completion_timeout(&ctx->aborted, -- msecs_to_jiffies(10000)); -- if (ret == 0) { -- dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -- force_abort(ctx); -- } --} -- --void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- __ipu_image_convert_abort(ctx); -- ctx->aborting = false; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -- --/* Unprepare image conversion context */ --void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned long flags; -- bool put_res; -- -- /* make sure no runs are hanging around */ -- __ipu_image_convert_abort(ctx); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- list_del(&ctx->list); -- -- put_res = list_empty(&chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (put_res) -- release_ipu_resources(chan); -- -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- free_dma_buf(priv, &ctx->rot_intermediate[0]); -- -- kfree(ctx); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -- --/* -- * "Canned" asynchronous single image conversion. Allocates and returns -- * a new conversion run. On successful return the caller must free the -- * run and call ipu_image_convert_unprepare() after conversion completes. -- */ --struct ipu_image_convert_run * --ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- int ret; -- -- ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -- complete, complete_context); -- if (IS_ERR(ctx)) -- return ERR_CAST(ctx); -- -- run = kzalloc(sizeof(*run), GFP_KERNEL); -- if (!run) { -- ipu_image_convert_unprepare(ctx); -- return ERR_PTR(-ENOMEM); -- } -- -- run->ctx = ctx; -- run->in_phys = in->phys0; -- run->out_phys = out->phys0; -- -- ret = ipu_image_convert_queue(run); -- if (ret) { -- ipu_image_convert_unprepare(ctx); -- kfree(run); -- return ERR_PTR(ret); -- } -- -- return run; --} --EXPORT_SYMBOL_GPL(ipu_image_convert); -- --/* "Canned" synchronous single image conversion */ --static void image_convert_sync_complete(struct ipu_image_convert_run *run, -- void *data) --{ -- struct completion *comp = data; -- -- complete(comp); --} -- --int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image_convert_run *run; -- struct completion comp; -- int ret; -- -- init_completion(&comp); -- -- run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -- image_convert_sync_complete, &comp); -- if (IS_ERR(run)) -- return PTR_ERR(run); -- -- ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -- ret = (ret == 0) ? -ETIMEDOUT : 0; -- -- ipu_image_convert_unprepare(run->ctx); -- kfree(run); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) --{ -- struct ipu_image_convert_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->image_convert_priv = priv; -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- struct ipu_image_convert_chan *chan = &priv->chan[i]; -- -- chan->ic_task = i; -- chan->priv = priv; -- chan->dma_ch = &image_convert_dma_chan[i]; -- chan->out_eof_irq = -1; -- chan->rot_out_eof_irq = -1; -- -- spin_lock_init(&chan->irqlock); -- INIT_LIST_HEAD(&chan->ctx_list); -- INIT_LIST_HEAD(&chan->pending_q); -- INIT_LIST_HEAD(&chan->done_q); -- } -- -- return 0; --} -- --void ipu_image_convert_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-pre.c -+++ /dev/null -@@ -1,346 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/genalloc.h> --#include <linux/module.h> --#include <linux/of.h> --#include <linux/platform_device.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --#define IPU_PRE_MAX_WIDTH 2048 --#define IPU_PRE_NUM_SCANLINES 8 -- --#define IPU_PRE_CTRL 0x000 --#define IPU_PRE_CTRL_SET 0x004 --#define IPU_PRE_CTRL_ENABLE (1 << 0) --#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) --#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) --#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) --#define IPU_PRE_CTRL_VFLIP (1 << 5) --#define IPU_PRE_CTRL_SO (1 << 6) --#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) --#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) --#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) --#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) --#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) --#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) --#define IPU_PRE_CTRL_CLKGATE (1 << 30) --#define IPU_PRE_CTRL_SFTRST (1 << 31) -- --#define IPU_PRE_CUR_BUF 0x030 -- --#define IPU_PRE_NEXT_BUF 0x040 -- --#define IPU_PRE_TPR_CTRL 0x070 --#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6) -- --#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 --#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) --#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) --#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) --#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) --#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) --#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -- --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 --#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_CTRL 0x110 --#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) --#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -- --#define IPU_PRE_STORE_ENG_STATUS 0x120 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -- --#define IPU_PRE_STORE_ENG_SIZE 0x130 --#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_PITCH 0x140 --#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -- --#define IPU_PRE_STORE_ENG_ADDR 0x150 -- --struct ipu_pre { -- struct list_head list; -- struct device *dev; -- -- void __iomem *regs; -- struct clk *clk_axi; -- struct gen_pool *iram; -- -- dma_addr_t buffer_paddr; -- void *buffer_virt; -- bool in_use; -- unsigned int safe_window_end; -- unsigned int last_bufaddr; --}; -- --static DEFINE_MUTEX(ipu_pre_list_mutex); --static LIST_HEAD(ipu_pre_list); --static int available_pres; -- --int ipu_pre_get_available_count(void) --{ -- return available_pres; --} -- --struct ipu_pre * --ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) --{ -- struct device_node *pre_node = of_parse_phandle(dev->of_node, -- name, index); -- struct ipu_pre *pre; -- -- mutex_lock(&ipu_pre_list_mutex); -- list_for_each_entry(pre, &ipu_pre_list, list) { -- if (pre_node == pre->dev->of_node) { -- mutex_unlock(&ipu_pre_list_mutex); -- device_link_add(dev, pre->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- of_node_put(pre_node); -- return pre; -- } -- } -- mutex_unlock(&ipu_pre_list_mutex); -- -- of_node_put(pre_node); -- -- return NULL; --} -- --int ipu_pre_get(struct ipu_pre *pre) --{ -- u32 val; -- -- if (pre->in_use) -- return -EBUSY; -- -- /* first get the engine out of reset and remove clock gating */ -- writel(0, pre->regs + IPU_PRE_CTRL); -- -- /* init defaults that should be applied to all streams */ -- val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -- IPU_PRE_CTRL_HANDSHAKE_EN | -- IPU_PRE_CTRL_TPR_REST_SEL | -- IPU_PRE_CTRL_SDW_UPDATE; -- writel(val, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = true; -- return 0; --} -- --void ipu_pre_put(struct ipu_pre *pre) --{ -- writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = false; --} -- --void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -- unsigned int height, unsigned int stride, u32 format, -- uint64_t modifier, unsigned int bufaddr) --{ -- const struct drm_format_info *info = drm_format_info(format); -- u32 active_bpp = info->cpp[0] >> 1; -- u32 val; -- -- /* calculate safe window for ctrl register updates */ -- if (modifier == DRM_FORMAT_MOD_LINEAR) -- pre->safe_window_end = height - 2; -- else -- pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1; -- -- writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); -- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -- pre->last_bufaddr = bufaddr; -- -- val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -- IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -- IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -- IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -- -- val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -- IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -- -- val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -- -- val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -- IPU_PRE_STORE_ENG_CTRL_STORE_EN; -- writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -- -- val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -- IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -- -- val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -- -- writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); -- -- val = readl(pre->regs + IPU_PRE_TPR_CTRL); -- val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK; -- if (modifier != DRM_FORMAT_MOD_LINEAR) { -- /* only support single buffer formats for now */ -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF; -- if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED; -- if (info->cpp[0] == 2) -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT; -- } -- writel(val, pre->regs + IPU_PRE_TPR_CTRL); -- -- val = readl(pre->regs + IPU_PRE_CTRL); -- val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | -- IPU_PRE_CTRL_SDW_UPDATE; -- if (modifier == DRM_FORMAT_MOD_LINEAR) -- val &= ~IPU_PRE_CTRL_BLOCK_EN; -- else -- val |= IPU_PRE_CTRL_BLOCK_EN; -- writel(val, pre->regs + IPU_PRE_CTRL); --} -- --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) --{ -- unsigned long timeout = jiffies + msecs_to_jiffies(5); -- unsigned short current_yblock; -- u32 val; -- -- if (bufaddr == pre->last_bufaddr) -- return; -- -- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -- pre->last_bufaddr = bufaddr; -- -- do { -- if (time_after(jiffies, timeout)) { -- dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -- return; -- } -- -- val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -- current_yblock = -- (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -- IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -- } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -- -- writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); --} -- --bool ipu_pre_update_pending(struct ipu_pre *pre) --{ -- return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -- IPU_PRE_CTRL_SDW_UPDATE); --} -- --u32 ipu_pre_get_baddr(struct ipu_pre *pre) --{ -- return (u32)pre->buffer_paddr; --} -- --static int ipu_pre_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_pre *pre; -- -- pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -- if (!pre) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- pre->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(pre->regs)) -- return PTR_ERR(pre->regs); -- -- pre->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(pre->clk_axi)) -- return PTR_ERR(pre->clk_axi); -- -- pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -- if (!pre->iram) -- return -EPROBE_DEFER; -- -- /* -- * Allocate IRAM buffer with maximum size. This could be made dynamic, -- * but as there is no other user of this IRAM region and we can fit all -- * max sized buffers into it, there is no need yet. -- */ -- pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -- IPU_PRE_NUM_SCANLINES * 4, -- &pre->buffer_paddr); -- if (!pre->buffer_virt) -- return -ENOMEM; -- -- clk_prepare_enable(pre->clk_axi); -- -- pre->dev = dev; -- platform_set_drvdata(pdev, pre); -- mutex_lock(&ipu_pre_list_mutex); -- list_add(&pre->list, &ipu_pre_list); -- available_pres++; -- mutex_unlock(&ipu_pre_list_mutex); -- -- return 0; --} -- --static int ipu_pre_remove(struct platform_device *pdev) --{ -- struct ipu_pre *pre = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_pre_list_mutex); -- list_del(&pre->list); -- available_pres--; -- mutex_unlock(&ipu_pre_list_mutex); -- -- clk_disable_unprepare(pre->clk_axi); -- -- if (pre->buffer_virt) -- gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -- IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -- return 0; --} -- --static const struct of_device_id ipu_pre_dt_ids[] = { -- { .compatible = "fsl,imx6qp-pre", }, -- { /* sentinel */ }, --}; -- --struct platform_driver ipu_pre_drv = { -- .probe = ipu_pre_probe, -- .remove = ipu_pre_remove, -- .driver = { -- .name = "imx-ipu-pre", -- .of_match_table = ipu_pre_dt_ids, -- }, --}; ---- a/drivers/gpu/ipu-v3/ipu-prg.c -+++ /dev/null -@@ -1,483 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/iopoll.h> --#include <linux/mfd/syscon.h> --#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> --#include <linux/module.h> --#include <linux/of.h> --#include <linux/platform_device.h> --#include <linux/pm_runtime.h> --#include <linux/regmap.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --#define IPU_PRG_CTL 0x00 --#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) --#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 --#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) --#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) --#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) --#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) --#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) --#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) --#define IPU_PRG_CTL_SOFTRST (1 << 30) --#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -- --#define IPU_PRG_STATUS 0x04 --#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) --#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -- --#define IPU_PRG_QOS 0x08 --#define IPU_PRG_QOS_ARID_MASK 0xf --#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -- --#define IPU_PRG_REG_UPDATE 0x0c --#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -- --#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) --#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -- --#define IPU_PRG_CROP_LINE 0x1c -- --#define IPU_PRG_THD 0x20 -- --#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -- --#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -- --#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -- --#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) --#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 --#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -- --struct ipu_prg_channel { -- bool enabled; -- int used_pre; --}; -- --struct ipu_prg { -- struct list_head list; -- struct device *dev; -- int id; -- -- void __iomem *regs; -- struct clk *clk_ipg, *clk_axi; -- struct regmap *iomuxc_gpr; -- struct ipu_pre *pres[3]; -- -- struct ipu_prg_channel chan[3]; --}; -- --static DEFINE_MUTEX(ipu_prg_list_mutex); --static LIST_HEAD(ipu_prg_list); -- --struct ipu_prg * --ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) --{ -- struct device_node *prg_node = of_parse_phandle(dev->of_node, -- name, 0); -- struct ipu_prg *prg; -- -- mutex_lock(&ipu_prg_list_mutex); -- list_for_each_entry(prg, &ipu_prg_list, list) { -- if (prg_node == prg->dev->of_node) { -- mutex_unlock(&ipu_prg_list_mutex); -- device_link_add(dev, prg->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- prg->id = ipu_id; -- of_node_put(prg_node); -- return prg; -- } -- } -- mutex_unlock(&ipu_prg_list_mutex); -- -- of_node_put(prg_node); -- -- return NULL; --} -- --int ipu_prg_max_active_channels(void) --{ -- return ipu_pre_get_available_count(); --} --EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -- --bool ipu_prg_present(struct ipu_soc *ipu) --{ -- if (ipu->prg_priv) -- return true; -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_prg_present); -- --bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -- uint64_t modifier) --{ -- const struct drm_format_info *info = drm_format_info(format); -- -- if (info->num_planes != 1) -- return false; -- -- switch (modifier) { -- case DRM_FORMAT_MOD_LINEAR: -- case DRM_FORMAT_MOD_VIVANTE_TILED: -- case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: -- return true; -- default: -- return false; -- } --} --EXPORT_SYMBOL_GPL(ipu_prg_format_supported); -- --int ipu_prg_enable(struct ipu_soc *ipu) --{ -- struct ipu_prg *prg = ipu->prg_priv; -- -- if (!prg) -- return 0; -- -- return pm_runtime_get_sync(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_enable); -- --void ipu_prg_disable(struct ipu_soc *ipu) --{ -- struct ipu_prg *prg = ipu->prg_priv; -- -- if (!prg) -- return; -- -- pm_runtime_put(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_disable); -- --/* -- * The channel configuartion functions below are not thread safe, as they -- * must be only called from the atomic commit path in the DRM driver, which -- * is properly serialized. -- */ --static int ipu_prg_ipu_to_prg_chan(int ipu_chan) --{ -- /* -- * This isn't clearly documented in the RM, but IPU to PRG channel -- * assignment is fixed, as only with this mapping the control signals -- * match up. -- */ -- switch (ipu_chan) { -- case IPUV3_CHANNEL_MEM_BG_SYNC: -- return 0; -- case IPUV3_CHANNEL_MEM_FG_SYNC: -- return 1; -- case IPUV3_CHANNEL_MEM_DC_SYNC: -- return 2; -- default: -- return -EINVAL; -- } --} -- --static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) --{ -- int i, ret; -- -- /* channel 0 is special as it is hardwired to one of the PREs */ -- if (prg_chan == 0) { -- ret = ipu_pre_get(prg->pres[0]); -- if (ret) -- goto fail; -- prg->chan[prg_chan].used_pre = 0; -- return 0; -- } -- -- for (i = 1; i < 3; i++) { -- ret = ipu_pre_get(prg->pres[i]); -- if (!ret) { -- u32 val, mux; -- int shift; -- -- prg->chan[prg_chan].used_pre = i; -- -- /* configure the PRE to PRG channel mux */ -- shift = (i == 1) ? 12 : 14; -- mux = (prg->id << 1) | (prg_chan - 1); -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, mux << shift); -- -- /* check other mux, must not point to same channel */ -- shift = (i == 1) ? 14 : 12; -- regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -- if (((val >> shift) & 0x3) == mux) { -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, -- (mux ^ 0x1) << shift); -- } -- -- return 0; -- } -- } -- --fail: -- dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -- return ret; --} -- --static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) --{ -- struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -- -- ipu_pre_put(prg->pres[chan->used_pre]); -- chan->used_pre = -1; --} -- --void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- u32 val; -- -- if (prg_chan < 0) -- return; -- -- chan = &prg->chan[prg_chan]; -- if (!chan->enabled) -- return; -- -- pm_runtime_get_sync(prg->dev); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- pm_runtime_put(prg->dev); -- -- ipu_prg_put_pre(prg, prg_chan); -- -- chan->enabled = false; --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); -- --int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, -- unsigned int axi_id, unsigned int width, -- unsigned int height, unsigned int stride, -- u32 format, uint64_t modifier, unsigned long *eba) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- u32 val; -- int ret; -- -- if (prg_chan < 0) -- return prg_chan; -- -- chan = &prg->chan[prg_chan]; -- -- if (chan->enabled) { -- ipu_pre_update(prg->pres[chan->used_pre], *eba); -- return 0; -- } -- -- ret = ipu_prg_get_pre(prg, prg_chan); -- if (ret) -- return ret; -- -- ipu_pre_configure(prg->pres[chan->used_pre], -- width, height, stride, format, modifier, *eba); -- -- -- pm_runtime_get_sync(prg->dev); -- -- val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -- writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -- -- val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -- ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -- writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -- -- val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -- *eba = val; -- writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- /* config AXI ID */ -- val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -- IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -- val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -- /* enable channel */ -- val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- /* wait for both double buffers to be filled */ -- readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -- (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -- (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -- 5, 1000); -- -- pm_runtime_put(prg->dev); -- -- chan->enabled = true; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -- --bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- -- if (prg_chan < 0) -- return false; -- -- chan = &prg->chan[prg_chan]; -- WARN_ON(!chan->enabled); -- -- return ipu_pre_update_pending(prg->pres[chan->used_pre]); --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -- --static int ipu_prg_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_prg *prg; -- u32 val; -- int i, ret; -- -- prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -- if (!prg) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- prg->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(prg->regs)) -- return PTR_ERR(prg->regs); -- -- -- prg->clk_ipg = devm_clk_get(dev, "ipg"); -- if (IS_ERR(prg->clk_ipg)) -- return PTR_ERR(prg->clk_ipg); -- -- prg->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(prg->clk_axi)) -- return PTR_ERR(prg->clk_axi); -- -- prg->iomuxc_gpr = -- syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -- if (IS_ERR(prg->iomuxc_gpr)) -- return PTR_ERR(prg->iomuxc_gpr); -- -- for (i = 0; i < 3; i++) { -- prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -- if (!prg->pres[i]) -- return -EPROBE_DEFER; -- } -- -- ret = clk_prepare_enable(prg->clk_ipg); -- if (ret) -- return ret; -- -- ret = clk_prepare_enable(prg->clk_axi); -- if (ret) { -- clk_disable_unprepare(prg->clk_ipg); -- return ret; -- } -- -- /* init to free running mode */ -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_SHADOW_EN; -- writel(val, prg->regs + IPU_PRG_CTL); -- -- /* disable address threshold */ -- writel(0xffffffff, prg->regs + IPU_PRG_THD); -- -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -- -- prg->dev = dev; -- platform_set_drvdata(pdev, prg); -- mutex_lock(&ipu_prg_list_mutex); -- list_add(&prg->list, &ipu_prg_list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- return 0; --} -- --static int ipu_prg_remove(struct platform_device *pdev) --{ -- struct ipu_prg *prg = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_prg_list_mutex); -- list_del(&prg->list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- return 0; --} -- --#ifdef CONFIG_PM --static int prg_suspend(struct device *dev) --{ -- struct ipu_prg *prg = dev_get_drvdata(dev); -- -- clk_disable_unprepare(prg->clk_axi); -- clk_disable_unprepare(prg->clk_ipg); -- -- return 0; --} -- --static int prg_resume(struct device *dev) --{ -- struct ipu_prg *prg = dev_get_drvdata(dev); -- int ret; -- -- ret = clk_prepare_enable(prg->clk_ipg); -- if (ret) -- return ret; -- -- ret = clk_prepare_enable(prg->clk_axi); -- if (ret) { -- clk_disable_unprepare(prg->clk_ipg); -- return ret; -- } -- -- return 0; --} --#endif -- --static const struct dev_pm_ops prg_pm_ops = { -- SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) --}; -- --static const struct of_device_id ipu_prg_dt_ids[] = { -- { .compatible = "fsl,imx6qp-prg", }, -- { /* sentinel */ }, --}; -- --struct platform_driver ipu_prg_drv = { -- .probe = ipu_prg_probe, -- .remove = ipu_prg_remove, -- .driver = { -- .name = "imx-ipu-prg", -- .pm = &prg_pm_ops, -- .of_match_table = ipu_prg_dt_ids, -- }, --}; ---- a/drivers/gpu/ipu-v3/ipu-prv.h -+++ /dev/null -@@ -1,274 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-or-later */ --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#ifndef __IPU_PRV_H__ --#define __IPU_PRV_H__ -- --struct ipu_soc; -- --#include <linux/types.h> --#include <linux/device.h> --#include <linux/clk.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> -- --#define IPU_MCU_T_DEFAULT 8 --#define IPU_CM_IDMAC_REG_OFS 0x00008000 --#define IPU_CM_IC_REG_OFS 0x00020000 --#define IPU_CM_IRT_REG_OFS 0x00028000 --#define IPU_CM_CSI0_REG_OFS 0x00030000 --#define IPU_CM_CSI1_REG_OFS 0x00038000 --#define IPU_CM_SMFC_REG_OFS 0x00050000 --#define IPU_CM_DC_REG_OFS 0x00058000 --#define IPU_CM_DMFC_REG_OFS 0x00060000 -- --/* Register addresses */ --/* IPU Common registers */ --#define IPU_CM_REG(offset) (offset) -- --#define IPU_CONF IPU_CM_REG(0) -- --#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) --#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) --#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) --#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) --#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) --#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) --#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) --#define IPU_SKIP IPU_CM_REG(0x00bc) --#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) --#define IPU_DISP_GEN IPU_CM_REG(0x00c4) --#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) --#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) --#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) --#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) --#define IPU_SNOOP IPU_CM_REG(0x00d8) --#define IPU_MEM_RST IPU_CM_REG(0x00dc) --#define IPU_PM IPU_CM_REG(0x00e0) --#define IPU_GPR IPU_CM_REG(0x00e4) --#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) --#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) --#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) --#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) --#define IPU_SRM_STAT IPU_CM_REG(0x024C) --#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) --#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) --#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -- --#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) --#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -- --/* SRM_PRI2 */ --#define DP_S_SRM_MODE_MASK (0x3 << 3) --#define DP_S_SRM_MODE_NOW (0x3 << 3) --#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -- --/* FS_PROC_FLOW1 */ --#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) --#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) --#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) --#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) --#define FS_PP_SRC_SEL_MASK (0xf << 12) --#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) --#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) --#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) --#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) --#define FS_PRP_SRC_SEL_MASK (0xf << 24) --#define FS_VDI_SRC_SEL_MASK (0x3 << 28) --#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) --#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -- --/* FS_PROC_FLOW2 */ --#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) --#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) --#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) --#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) --#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) --#define FS_PP_DEST_SEL_MASK (0xf << 12) --#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) --#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) --#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) --#define FS_PRP_DEST_SEL_MASK (0xf << 24) -- --#define IPU_DI0_COUNTER_RELEASE (1 << 24) --#define IPU_DI1_COUNTER_RELEASE (1 << 25) -- --#define IPU_IDMAC_REG(offset) (offset) -- --#define IDMAC_CONF IPU_IDMAC_REG(0x0000) --#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) --#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) --#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) --#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) --#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) --#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) --#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) --#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) --#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) --#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) --#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) --#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -- --#define IPU_NUM_IRQS (32 * 15) -- --enum ipu_modules { -- IPU_CONF_CSI0_EN = (1 << 0), -- IPU_CONF_CSI1_EN = (1 << 1), -- IPU_CONF_IC_EN = (1 << 2), -- IPU_CONF_ROT_EN = (1 << 3), -- IPU_CONF_ISP_EN = (1 << 4), -- IPU_CONF_DP_EN = (1 << 5), -- IPU_CONF_DI0_EN = (1 << 6), -- IPU_CONF_DI1_EN = (1 << 7), -- IPU_CONF_SMFC_EN = (1 << 8), -- IPU_CONF_DC_EN = (1 << 9), -- IPU_CONF_DMFC_EN = (1 << 10), -- -- IPU_CONF_VDI_EN = (1 << 12), -- -- IPU_CONF_IDMAC_DIS = (1 << 22), -- -- IPU_CONF_IC_DMFC_SEL = (1 << 25), -- IPU_CONF_IC_DMFC_SYNC = (1 << 26), -- IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -- -- IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -- IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -- IPU_CONF_IC_INPUT = (1 << 30), -- IPU_CONF_CSI_SEL = (1 << 31), --}; -- --struct ipuv3_channel { -- unsigned int num; -- struct ipu_soc *ipu; -- struct list_head list; --}; -- --struct ipu_cpmem; --struct ipu_csi; --struct ipu_dc_priv; --struct ipu_dmfc_priv; --struct ipu_di; --struct ipu_ic_priv; --struct ipu_vdi; --struct ipu_image_convert_priv; --struct ipu_smfc_priv; --struct ipu_pre; --struct ipu_prg; -- --struct ipu_devtype; -- --struct ipu_soc { -- struct device *dev; -- const struct ipu_devtype *devtype; -- enum ipuv3_type ipu_type; -- spinlock_t lock; -- struct mutex channel_lock; -- struct list_head channels; -- -- void __iomem *cm_reg; -- void __iomem *idmac_reg; -- -- int id; -- int usecount; -- -- struct clk *clk; -- -- int irq_sync; -- int irq_err; -- struct irq_domain *domain; -- -- struct ipu_cpmem *cpmem_priv; -- struct ipu_dc_priv *dc_priv; -- struct ipu_dp_priv *dp_priv; -- struct ipu_dmfc_priv *dmfc_priv; -- struct ipu_di *di_priv[2]; -- struct ipu_csi *csi_priv[2]; -- struct ipu_ic_priv *ic_priv; -- struct ipu_vdi *vdi_priv; -- struct ipu_image_convert_priv *image_convert_priv; -- struct ipu_smfc_priv *smfc_priv; -- struct ipu_prg *prg_priv; --}; -- --static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->idmac_reg + offset); --} -- --static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -- unsigned offset) --{ -- writel(value, ipu->idmac_reg + offset); --} -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask); --int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu); --void ipu_csi_exit(struct ipu_soc *ipu, int id); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base); --void ipu_ic_exit(struct ipu_soc *ipu); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module); --void ipu_vdi_exit(struct ipu_soc *ipu); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); --void ipu_image_convert_exit(struct ipu_soc *ipu); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *ipu_clk); --void ipu_di_exit(struct ipu_soc *ipu, int id); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk); --void ipu_dmfc_exit(struct ipu_soc *ipu); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_dp_exit(struct ipu_soc *ipu); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- unsigned long template_base); --void ipu_dc_exit(struct ipu_soc *ipu); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_cpmem_exit(struct ipu_soc *ipu); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_smfc_exit(struct ipu_soc *ipu); -- --struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -- int index); --int ipu_pre_get_available_count(void); --int ipu_pre_get(struct ipu_pre *pre); --void ipu_pre_put(struct ipu_pre *pre); --u32 ipu_pre_get_baddr(struct ipu_pre *pre); --void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -- unsigned int height, unsigned int stride, u32 format, -- uint64_t modifier, unsigned int bufaddr); --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); --bool ipu_pre_update_pending(struct ipu_pre *pre); -- --struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -- int ipu_id); -- --extern struct platform_driver ipu_pre_drv; --extern struct platform_driver ipu_prg_drv; -- --#endif /* __IPU_PRV_H__ */ ---- a/drivers/gpu/ipu-v3/ipu-smfc.c -+++ /dev/null -@@ -1,202 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/init.h> --#include <linux/io.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/clk.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --struct ipu_smfc { -- struct ipu_smfc_priv *priv; -- int chno; -- bool inuse; --}; -- --struct ipu_smfc_priv { -- void __iomem *base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- struct ipu_smfc channel[4]; -- int use_count; --}; -- --/*SMFC Registers */ --#define SMFC_MAP 0x0000 --#define SMFC_WMC 0x0004 --#define SMFC_BS 0x0008 -- --int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 4; -- val = readl(priv->base + SMFC_BS); -- val &= ~(0xf << shift); -- val |= burstsize << shift; -- writel(val, priv->base + SMFC_BS); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -- --int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 3; -- val = readl(priv->base + SMFC_MAP); -- val &= ~(0x7 << shift); -- val |= ((csi_id << 2) | mipi_id) << shift; -- writel(val, priv->base + SMFC_MAP); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -- --int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -- val = readl(priv->base + SMFC_WMC); -- val &= ~(0x3f << shift); -- val |= ((clr_level << 3) | set_level) << shift; -- writel(val, priv->base + SMFC_WMC); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -- --int ipu_smfc_enable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -- -- priv->use_count++; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_enable); -- --int ipu_smfc_disable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_disable); -- --struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) --{ -- struct ipu_smfc_priv *priv = ipu->smfc_priv; -- struct ipu_smfc *smfc, *ret; -- unsigned long flags; -- -- if (chno >= 4) -- return ERR_PTR(-EINVAL); -- -- smfc = &priv->channel[chno]; -- ret = smfc; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (smfc->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- smfc->inuse = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_smfc_get); -- --void ipu_smfc_put(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- smfc->inuse = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_smfc_put); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base) --{ -- struct ipu_smfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->smfc_priv = priv; -- spin_lock_init(&priv->lock); -- priv->ipu = ipu; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- for (i = 0; i < 4; i++) { -- priv->channel[i].priv = priv; -- priv->channel[i].chno = i; -- } -- -- pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -- -- return 0; --} -- --void ipu_smfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/ipu-v3/ipu-vdi.c -+++ /dev/null -@@ -1,234 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/io.h> --#include "ipu-prv.h" -- --struct ipu_vdi { -- void __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- -- --/* VDI Register Offsets */ --#define VDI_FSIZE 0x0000 --#define VDI_C 0x0004 -- --/* VDI Register Fields */ --#define VDI_C_CH_420 (0 << 1) --#define VDI_C_CH_422 (1 << 1) --#define VDI_C_MOT_SEL_MASK (0x3 << 2) --#define VDI_C_MOT_SEL_FULL (2 << 2) --#define VDI_C_MOT_SEL_LOW (1 << 2) --#define VDI_C_MOT_SEL_MED (0 << 2) --#define VDI_C_BURST_SIZE1_4 (3 << 4) --#define VDI_C_BURST_SIZE2_4 (3 << 8) --#define VDI_C_BURST_SIZE3_4 (3 << 12) --#define VDI_C_BURST_SIZE_MASK 0xF --#define VDI_C_BURST_SIZE1_OFFSET 4 --#define VDI_C_BURST_SIZE2_OFFSET 8 --#define VDI_C_BURST_SIZE3_OFFSET 12 --#define VDI_C_VWM1_SET_1 (0 << 16) --#define VDI_C_VWM1_SET_2 (1 << 16) --#define VDI_C_VWM1_CLR_2 (1 << 19) --#define VDI_C_VWM3_SET_1 (0 << 22) --#define VDI_C_VWM3_SET_2 (1 << 22) --#define VDI_C_VWM3_CLR_2 (1 << 25) --#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) --#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -- --static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) --{ -- return readl(vdi->base + offset); --} -- --static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -- unsigned int offset) --{ -- writel(value, vdi->base + offset); --} -- --void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) --{ -- bool top_field_0 = false; -- unsigned long flags; -- u32 reg; -- -- switch (field) { -- case V4L2_FIELD_INTERLACED_TB: -- case V4L2_FIELD_SEQ_TB: -- case V4L2_FIELD_TOP: -- top_field_0 = true; -- break; -- case V4L2_FIELD_INTERLACED_BT: -- case V4L2_FIELD_SEQ_BT: -- case V4L2_FIELD_BOTTOM: -- top_field_0 = false; -- break; -- default: -- top_field_0 = (std & V4L2_STD_525_60) ? true : false; -- break; -- } -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- if (top_field_0) -- reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -- else -- reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -- --void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- -- reg &= ~VDI_C_MOT_SEL_MASK; -- -- switch (motion_sel) { -- case MED_MOTION: -- reg |= VDI_C_MOT_SEL_MED; -- break; -- case HIGH_MOTION: -- reg |= VDI_C_MOT_SEL_FULL; -- break; -- default: -- reg |= VDI_C_MOT_SEL_LOW; -- break; -- } -- -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -- --void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) --{ -- unsigned long flags; -- u32 pixel_fmt, reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ((yres - 1) << 16) | (xres - 1); -- ipu_vdi_write(vdi, reg, VDI_FSIZE); -- -- /* -- * Full motion, only vertical filter is used. -- * Burst size is 4 accesses -- */ -- if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -- code == MEDIA_BUS_FMT_UYVY8_1X16 || -- code == MEDIA_BUS_FMT_YUYV8_2X8 || -- code == MEDIA_BUS_FMT_YUYV8_1X16) -- pixel_fmt = VDI_C_CH_422; -- else -- pixel_fmt = VDI_C_CH_420; -- -- reg = ipu_vdi_read(vdi, VDI_C); -- reg |= pixel_fmt; -- reg |= VDI_C_BURST_SIZE2_4; -- reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -- reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_setup); -- --void ipu_vdi_unsetup(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- ipu_vdi_write(vdi, 0, VDI_FSIZE); -- ipu_vdi_write(vdi, 0, VDI_C); -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -- --int ipu_vdi_enable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (!vdi->use_count) -- ipu_module_enable(vdi->ipu, vdi->module); -- -- vdi->use_count++; -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_enable); -- --int ipu_vdi_disable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (vdi->use_count) { -- if (!--vdi->use_count) -- ipu_module_disable(vdi->ipu, vdi->module); -- } -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_disable); -- --struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) --{ -- return ipu->vdi_priv; --} --EXPORT_SYMBOL_GPL(ipu_vdi_get); -- --void ipu_vdi_put(struct ipu_vdi *vdi) --{ --} --EXPORT_SYMBOL_GPL(ipu_vdi_put); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module) --{ -- struct ipu_vdi *vdi; -- -- vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -- if (!vdi) -- return -ENOMEM; -- -- ipu->vdi_priv = vdi; -- -- spin_lock_init(&vdi->lock); -- vdi->module = module; -- vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!vdi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -- vdi->ipu = ipu; -- -- return 0; --} -- --void ipu_vdi_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/video/Kconfig -+++ b/drivers/video/Kconfig -@@ -15,7 +15,7 @@ source "drivers/char/agp/Kconfig" - source "drivers/gpu/vga/Kconfig" - - source "drivers/gpu/host1x/Kconfig" --source "drivers/gpu/ipu-v3/Kconfig" -+source "drivers/gpu/imx/Kconfig" - - source "drivers/gpu/drm/Kconfig" - diff --git a/target/linux/layerscape/patches-5.4/805-display-0044-drm-imx-Extract-IPUv3-specific-KMS-functions-to-ipuv.patch b/target/linux/layerscape/patches-5.4/805-display-0044-drm-imx-Extract-IPUv3-specific-KMS-functions-to-ipuv.patch deleted file mode 100644 index cd155a572e..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0044-drm-imx-Extract-IPUv3-specific-KMS-functions-to-ipuv.patch +++ /dev/null @@ -1,142 +0,0 @@ -From 6b1bc8ad2bdcfa0cbda91a0963aed746c424465d Mon Sep 17 00:00:00 2001 -From: Liu Ying <victor.liu@nxp.com> -Date: Thu, 24 Jan 2019 17:06:30 +0800 -Subject: [PATCH] drm/imx: Extract IPUv3 specific KMS functions to ipuv3-kms.c - (part 1) - -Since we want to add i.MX DPU support into imx-drm, the imx-drm core -driver should be no more IPUv3 specific. Let's make imx-drm more generic -and extract IPUv3 specific KMS functions to ipuv3-kms.c. - -Signed-off-by: Liu Ying <victor.liu@nxp.com> -[ Aisheng: update to new kernel version accordingly ] -[ Aisheng: split IPU changes out of imx-drm-core change ] -Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> ---- - drivers/gpu/drm/imx/imx-drm-core.c | 81 -------------------------------------- - drivers/gpu/drm/imx/imx-drm.h | 5 +-- - 2 files changed, 2 insertions(+), 84 deletions(-) - ---- a/drivers/gpu/drm/imx/imx-drm-core.c -+++ b/drivers/gpu/drm/imx/imx-drm-core.c -@@ -26,9 +26,6 @@ - #include <drm/drm_vblank.h> - - #include "imx-drm.h" --#include "ipuv3-plane.h" -- --#define MAX_CRTC 4 - - static int legacyfb_depth = 16; - module_param(legacyfb_depth, int, 0444); -@@ -48,81 +45,6 @@ void imx_drm_encoder_destroy(struct drm_ - } - EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); - --static int imx_drm_atomic_check(struct drm_device *dev, -- struct drm_atomic_state *state) --{ -- int ret; -- -- ret = drm_atomic_helper_check(dev, state); -- if (ret) -- return ret; -- -- /* -- * Check modeset again in case crtc_state->mode_changed is -- * updated in plane's ->atomic_check callback. -- */ -- ret = drm_atomic_helper_check_modeset(dev, state); -- if (ret) -- return ret; -- -- /* Assign PRG/PRE channels and check if all constrains are satisfied. */ -- ret = ipu_planes_assign_pre(dev, state); -- if (ret) -- return ret; -- -- return ret; --} -- --static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { -- .fb_create = drm_gem_fb_create, -- .atomic_check = imx_drm_atomic_check, -- .atomic_commit = drm_atomic_helper_commit, --}; -- --static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) --{ -- struct drm_device *dev = state->dev; -- struct drm_plane *plane; -- struct drm_plane_state *old_plane_state, *new_plane_state; -- bool plane_disabling = false; -- int i; -- -- drm_atomic_helper_commit_modeset_disables(dev, state); -- -- drm_atomic_helper_commit_planes(dev, state, -- DRM_PLANE_COMMIT_ACTIVE_ONLY | -- DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET); -- -- drm_atomic_helper_commit_modeset_enables(dev, state); -- -- for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { -- if (drm_atomic_plane_disabling(old_plane_state, new_plane_state)) -- plane_disabling = true; -- } -- -- /* -- * The flip done wait is only strictly required by imx-drm if a deferred -- * plane disable is in-flight. As the core requires blocking commits -- * to wait for the flip it is done here unconditionally. This keeps the -- * workitem around a bit longer than required for the majority of -- * non-blocking commits, but we accept that for the sake of simplicity. -- */ -- drm_atomic_helper_wait_for_flip_done(dev, state); -- -- if (plane_disabling) { -- for_each_old_plane_in_state(state, plane, old_plane_state, i) -- ipu_plane_disable_deferred(plane); -- -- } -- -- drm_atomic_helper_commit_hw_done(state); --} -- --static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { -- .atomic_commit_tail = imx_drm_atomic_commit_tail, --}; -- -- - int imx_drm_encoder_parse_of(struct drm_device *drm, - struct drm_encoder *encoder, struct device_node *np) - { -@@ -223,9 +145,6 @@ static int imx_drm_bind(struct device *d - drm->mode_config.min_height = 1; - drm->mode_config.max_width = 4096; - drm->mode_config.max_height = 4096; -- drm->mode_config.funcs = &imx_drm_mode_config_funcs; -- drm->mode_config.helper_private = &imx_drm_mode_config_helpers; -- drm->mode_config.allow_fb_modifiers = true; - drm->mode_config.normalize_zpos = true; - - drm_mode_config_init(drm); ---- a/drivers/gpu/drm/imx/imx-drm.h -+++ b/drivers/gpu/drm/imx/imx-drm.h -@@ -2,6 +2,8 @@ - #ifndef _IMX_DRM_H_ - #define _IMX_DRM_H_ - -+#define MAX_CRTC 4 -+ - struct device_node; - struct drm_crtc; - struct drm_connector; -@@ -38,7 +40,4 @@ int imx_drm_encoder_parse_of(struct drm_ - void imx_drm_connector_destroy(struct drm_connector *connector); - void imx_drm_encoder_destroy(struct drm_encoder *encoder); - --int ipu_planes_assign_pre(struct drm_device *dev, -- struct drm_atomic_state *state); -- - #endif /* _IMX_DRM_H_ */ diff --git a/target/linux/layerscape/patches-5.4/805-display-0045-drm-imx-Add-DPU-KMS-support-part-2.patch b/target/linux/layerscape/patches-5.4/805-display-0045-drm-imx-Add-DPU-KMS-support-part-2.patch index e00e80564d..ae32cf596f 100644 --- a/target/linux/layerscape/patches-5.4/805-display-0045-drm-imx-Add-DPU-KMS-support-part-2.patch +++ b/target/linux/layerscape/patches-5.4/805-display-0045-drm-imx-Add-DPU-KMS-support-part-2.patch @@ -20,8 +20,8 @@ Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com> +#include <video/dpu.h> #include "imx-drm.h" - -@@ -105,6 +106,10 @@ static int compare_of(struct device *dev + #include "ipuv3-plane.h" +@@ -183,6 +184,10 @@ static int compare_of(struct device *dev struct ipu_client_platformdata *pdata = dev->platform_data; return pdata->of_node == np; diff --git a/target/linux/layerscape/patches-5.4/805-display-0046-MLK-22171-drm-imx-Avoid-leaking-dangling-pointer-dev.patch b/target/linux/layerscape/patches-5.4/805-display-0046-MLK-22171-drm-imx-Avoid-leaking-dangling-pointer-dev.patch index b65727def6..b28f7b172c 100644 --- a/target/linux/layerscape/patches-5.4/805-display-0046-MLK-22171-drm-imx-Avoid-leaking-dangling-pointer-dev.patch +++ b/target/linux/layerscape/patches-5.4/805-display-0046-MLK-22171-drm-imx-Avoid-leaking-dangling-pointer-dev.patch @@ -17,7 +17,7 @@ Signed-off-by: Liu Ying <victor.liu@nxp.com> --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c -@@ -158,8 +158,6 @@ static int imx_drm_bind(struct device *d +@@ -239,8 +239,6 @@ static int imx_drm_bind(struct device *d if (ret) goto err_kms; @@ -26,7 +26,7 @@ Signed-off-by: Liu Ying <victor.liu@nxp.com> /* Now try and bind all our sub-components */ ret = component_bind_all(dev, drm); if (ret) -@@ -185,6 +183,8 @@ static int imx_drm_bind(struct device *d +@@ -266,6 +264,8 @@ static int imx_drm_bind(struct device *d drm_fbdev_generic_setup(drm, legacyfb_depth); diff --git a/target/linux/layerscape/patches-5.4/805-display-0047-MLK-18535-6-drm-imx-core-add-LCDIF-support.patch b/target/linux/layerscape/patches-5.4/805-display-0047-MLK-18535-6-drm-imx-core-add-LCDIF-support.patch index 6bf4e59dcf..96adbace75 100644 --- a/target/linux/layerscape/patches-5.4/805-display-0047-MLK-18535-6-drm-imx-core-add-LCDIF-support.patch +++ b/target/linux/layerscape/patches-5.4/805-display-0047-MLK-18535-6-drm-imx-core-add-LCDIF-support.patch @@ -22,7 +22,7 @@ Signed-off-by: Fancy Fang <chen.fang@nxp.com> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -@@ -110,6 +111,18 @@ static int compare_of(struct device *dev +@@ -188,6 +189,18 @@ static int compare_of(struct device *dev struct dpu_client_platformdata *pdata = dev->platform_data; return pdata->of_node == np; diff --git a/target/linux/layerscape/patches-5.4/805-display-0048-Revert-drm-imx-Extract-IPUv3-specific-KMS-functions-.patch b/target/linux/layerscape/patches-5.4/805-display-0048-Revert-drm-imx-Extract-IPUv3-specific-KMS-functions-.patch deleted file mode 100644 index 23411bc117..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0048-Revert-drm-imx-Extract-IPUv3-specific-KMS-functions-.patch +++ /dev/null @@ -1,135 +0,0 @@ -From d2943462cef18cb78680215dec058d5254e63dc3 Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 2 Mar 2020 13:45:41 +0800 -Subject: [PATCH] Revert "drm/imx: Extract IPUv3 specific KMS functions to - ipuv3-kms.c (part 1)" - -This reverts commit aad8cd9d3f10c57bbf405e25ed1be491d99b6294. ---- - drivers/gpu/drm/imx/imx-drm-core.c | 81 ++++++++++++++++++++++++++++++++++++++ - drivers/gpu/drm/imx/imx-drm.h | 5 ++- - 2 files changed, 84 insertions(+), 2 deletions(-) - ---- a/drivers/gpu/drm/imx/imx-drm-core.c -+++ b/drivers/gpu/drm/imx/imx-drm-core.c -@@ -28,6 +28,9 @@ - #include <video/dpu.h> - - #include "imx-drm.h" -+#include "ipuv3-plane.h" -+ -+#define MAX_CRTC 4 - - static int legacyfb_depth = 16; - module_param(legacyfb_depth, int, 0444); -@@ -47,6 +50,81 @@ void imx_drm_encoder_destroy(struct drm_ - } - EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); - -+static int imx_drm_atomic_check(struct drm_device *dev, -+ struct drm_atomic_state *state) -+{ -+ int ret; -+ -+ ret = drm_atomic_helper_check(dev, state); -+ if (ret) -+ return ret; -+ -+ /* -+ * Check modeset again in case crtc_state->mode_changed is -+ * updated in plane's ->atomic_check callback. -+ */ -+ ret = drm_atomic_helper_check_modeset(dev, state); -+ if (ret) -+ return ret; -+ -+ /* Assign PRG/PRE channels and check if all constrains are satisfied. */ -+ ret = ipu_planes_assign_pre(dev, state); -+ if (ret) -+ return ret; -+ -+ return ret; -+} -+ -+static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = { -+ .fb_create = drm_gem_fb_create, -+ .atomic_check = imx_drm_atomic_check, -+ .atomic_commit = drm_atomic_helper_commit, -+}; -+ -+static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state) -+{ -+ struct drm_device *dev = state->dev; -+ struct drm_plane *plane; -+ struct drm_plane_state *old_plane_state, *new_plane_state; -+ bool plane_disabling = false; -+ int i; -+ -+ drm_atomic_helper_commit_modeset_disables(dev, state); -+ -+ drm_atomic_helper_commit_planes(dev, state, -+ DRM_PLANE_COMMIT_ACTIVE_ONLY | -+ DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET); -+ -+ drm_atomic_helper_commit_modeset_enables(dev, state); -+ -+ for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { -+ if (drm_atomic_plane_disabling(old_plane_state, new_plane_state)) -+ plane_disabling = true; -+ } -+ -+ /* -+ * The flip done wait is only strictly required by imx-drm if a deferred -+ * plane disable is in-flight. As the core requires blocking commits -+ * to wait for the flip it is done here unconditionally. This keeps the -+ * workitem around a bit longer than required for the majority of -+ * non-blocking commits, but we accept that for the sake of simplicity. -+ */ -+ drm_atomic_helper_wait_for_flip_done(dev, state); -+ -+ if (plane_disabling) { -+ for_each_old_plane_in_state(state, plane, old_plane_state, i) -+ ipu_plane_disable_deferred(plane); -+ -+ } -+ -+ drm_atomic_helper_commit_hw_done(state); -+} -+ -+static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = { -+ .atomic_commit_tail = imx_drm_atomic_commit_tail, -+}; -+ -+ - int imx_drm_encoder_parse_of(struct drm_device *drm, - struct drm_encoder *encoder, struct device_node *np) - { -@@ -163,6 +241,9 @@ static int imx_drm_bind(struct device *d - drm->mode_config.min_height = 1; - drm->mode_config.max_width = 4096; - drm->mode_config.max_height = 4096; -+ drm->mode_config.funcs = &imx_drm_mode_config_funcs; -+ drm->mode_config.helper_private = &imx_drm_mode_config_helpers; -+ drm->mode_config.allow_fb_modifiers = true; - drm->mode_config.normalize_zpos = true; - - drm_mode_config_init(drm); ---- a/drivers/gpu/drm/imx/imx-drm.h -+++ b/drivers/gpu/drm/imx/imx-drm.h -@@ -2,8 +2,6 @@ - #ifndef _IMX_DRM_H_ - #define _IMX_DRM_H_ - --#define MAX_CRTC 4 -- - struct device_node; - struct drm_crtc; - struct drm_connector; -@@ -40,4 +38,7 @@ int imx_drm_encoder_parse_of(struct drm_ - void imx_drm_connector_destroy(struct drm_connector *connector); - void imx_drm_encoder_destroy(struct drm_encoder *encoder); - -+int ipu_planes_assign_pre(struct drm_device *dev, -+ struct drm_atomic_state *state); -+ - #endif /* _IMX_DRM_H_ */ diff --git a/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch b/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch deleted file mode 100644 index 1c1886b4f9..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0049-Revert-gpu-Move-ipu-v3-to-imx-folder.patch +++ /dev/null @@ -1,19991 +0,0 @@ -From 9a941f832d8b88fb29b40e4b3f9c2b42a8a9b000 Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 2 Mar 2020 13:46:13 +0800 -Subject: [PATCH] Revert "gpu: Move ipu-v3 to imx folder" - -This reverts commit 9da4beb1c9ce90f97c8d3b5c6254da576746d2cd. ---- - drivers/gpu/Makefile | 2 +- - drivers/gpu/imx/Kconfig | 1 - - drivers/gpu/imx/Makefile | 1 - - drivers/gpu/imx/ipu-v3/Kconfig | 11 - - drivers/gpu/imx/ipu-v3/Makefile | 10 - - drivers/gpu/imx/ipu-v3/ipu-common.c | 1565 ------------------ - drivers/gpu/imx/ipu-v3/ipu-cpmem.c | 976 ----------- - drivers/gpu/imx/ipu-v3/ipu-csi.c | 821 --------- - drivers/gpu/imx/ipu-v3/ipu-dc.c | 420 ----- - drivers/gpu/imx/ipu-v3/ipu-di.c | 745 --------- - drivers/gpu/imx/ipu-v3/ipu-dmfc.c | 214 --- - drivers/gpu/imx/ipu-v3/ipu-dp.c | 357 ---- - drivers/gpu/imx/ipu-v3/ipu-ic.c | 761 --------- - drivers/gpu/imx/ipu-v3/ipu-image-convert.c | 2475 ---------------------------- - drivers/gpu/imx/ipu-v3/ipu-pre.c | 346 ---- - drivers/gpu/imx/ipu-v3/ipu-prg.c | 483 ------ - drivers/gpu/imx/ipu-v3/ipu-prv.h | 274 --- - drivers/gpu/imx/ipu-v3/ipu-smfc.c | 202 --- - drivers/gpu/imx/ipu-v3/ipu-vdi.c | 234 --- - drivers/gpu/ipu-v3/Kconfig | 11 + - drivers/gpu/ipu-v3/Makefile | 10 + - drivers/gpu/ipu-v3/ipu-common.c | 1565 ++++++++++++++++++ - drivers/gpu/ipu-v3/ipu-cpmem.c | 976 +++++++++++ - drivers/gpu/ipu-v3/ipu-csi.c | 821 +++++++++ - drivers/gpu/ipu-v3/ipu-dc.c | 420 +++++ - drivers/gpu/ipu-v3/ipu-di.c | 745 +++++++++ - drivers/gpu/ipu-v3/ipu-dmfc.c | 214 +++ - drivers/gpu/ipu-v3/ipu-dp.c | 357 ++++ - drivers/gpu/ipu-v3/ipu-ic.c | 761 +++++++++ - drivers/gpu/ipu-v3/ipu-image-convert.c | 2475 ++++++++++++++++++++++++++++ - drivers/gpu/ipu-v3/ipu-pre.c | 346 ++++ - drivers/gpu/ipu-v3/ipu-prg.c | 483 ++++++ - drivers/gpu/ipu-v3/ipu-prv.h | 274 +++ - drivers/gpu/ipu-v3/ipu-smfc.c | 202 +++ - drivers/gpu/ipu-v3/ipu-vdi.c | 234 +++ - drivers/video/Kconfig | 2 +- - 36 files changed, 9896 insertions(+), 9898 deletions(-) - delete mode 100644 drivers/gpu/imx/Kconfig - delete mode 100644 drivers/gpu/imx/Makefile - delete mode 100644 drivers/gpu/imx/ipu-v3/Kconfig - delete mode 100644 drivers/gpu/imx/ipu-v3/Makefile - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-common.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-cpmem.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-csi.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-di.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dmfc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-dp.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-ic.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-image-convert.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-pre.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-prg.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-prv.h - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-smfc.c - delete mode 100644 drivers/gpu/imx/ipu-v3/ipu-vdi.c - create mode 100644 drivers/gpu/ipu-v3/Kconfig - create mode 100644 drivers/gpu/ipu-v3/Makefile - create mode 100644 drivers/gpu/ipu-v3/ipu-common.c - create mode 100644 drivers/gpu/ipu-v3/ipu-cpmem.c - create mode 100644 drivers/gpu/ipu-v3/ipu-csi.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-di.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dmfc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-dp.c - create mode 100644 drivers/gpu/ipu-v3/ipu-ic.c - create mode 100644 drivers/gpu/ipu-v3/ipu-image-convert.c - create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c - create mode 100644 drivers/gpu/ipu-v3/ipu-prg.c - create mode 100644 drivers/gpu/ipu-v3/ipu-prv.h - create mode 100644 drivers/gpu/ipu-v3/ipu-smfc.c - create mode 100644 drivers/gpu/ipu-v3/ipu-vdi.c - ---- a/drivers/gpu/Makefile -+++ b/drivers/gpu/Makefile -@@ -3,5 +3,5 @@ - # taken to initialize them in the correct order. Link order is the only way - # to ensure this currently. - obj-$(CONFIG_TEGRA_HOST1X) += host1x/ --obj-y += imx/ - obj-y += drm/ vga/ -+obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- a/drivers/gpu/imx/Kconfig -+++ /dev/null -@@ -1 +0,0 @@ --source "drivers/gpu/imx/ipu-v3/Kconfig" ---- a/drivers/gpu/imx/Makefile -+++ /dev/null -@@ -1 +0,0 @@ --obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ ---- a/drivers/gpu/imx/ipu-v3/Kconfig -+++ /dev/null -@@ -1,11 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0-only --config IMX_IPUV3_CORE -- tristate "IPUv3 core support" -- depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST -- depends on DRM || !DRM # if DRM=m, this can't be 'y' -- select BITREVERSE -- select GENERIC_ALLOCATOR if DRM -- select GENERIC_IRQ_CHIP -- help -- Choose this if you have a i.MX5/6 system and want to use the Image -- Processing Unit. This option only enables IPU base support. ---- a/drivers/gpu/imx/ipu-v3/Makefile -+++ /dev/null -@@ -1,10 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -- --imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -- ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -- ipu-image-convert.o ipu-smfc.o ipu-vdi.o -- --ifdef CONFIG_DRM -- imx-ipu-v3-objs += ipu-pre.o ipu-prg.o --endif ---- a/drivers/gpu/imx/ipu-v3/ipu-common.c -+++ /dev/null -@@ -1,1565 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/module.h> --#include <linux/export.h> --#include <linux/types.h> --#include <linux/reset.h> --#include <linux/platform_device.h> --#include <linux/err.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> --#include <linux/clk.h> --#include <linux/list.h> --#include <linux/irq.h> --#include <linux/irqchip/chained_irq.h> --#include <linux/irqdomain.h> --#include <linux/of_device.h> --#include <linux/of_graph.h> -- --#include <drm/drm_fourcc.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->cm_reg + offset); --} -- --static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) --{ -- writel(value, ipu->cm_reg + offset); --} -- --int ipu_get_num(struct ipu_soc *ipu) --{ -- return ipu->id; --} --EXPORT_SYMBOL_GPL(ipu_get_num); -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) --{ -- u32 val; -- -- val = ipu_cm_read(ipu, IPU_SRM_PRI2); -- val &= ~DP_S_SRM_MODE_MASK; -- val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -- DP_S_SRM_MODE_NOW; -- ipu_cm_write(ipu, val, IPU_SRM_PRI2); --} --EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -- --enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_ARGB1555: -- case DRM_FORMAT_ABGR1555: -- case DRM_FORMAT_RGBA5551: -- case DRM_FORMAT_BGRA5551: -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_ARGB4444: -- case DRM_FORMAT_XRGB8888: -- case DRM_FORMAT_XBGR8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- return IPUV3_COLORSPACE_RGB; -- case DRM_FORMAT_YUYV: -- case DRM_FORMAT_UYVY: -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- case DRM_FORMAT_NV12: -- case DRM_FORMAT_NV21: -- case DRM_FORMAT_NV16: -- case DRM_FORMAT_NV61: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -- --enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return IPUV3_COLORSPACE_YUV; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- return IPUV3_COLORSPACE_RGB; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -- --bool ipu_pixelformat_is_planar(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- return true; -- } -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -- --enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) --{ -- switch (mbus_code & 0xf000) { -- case 0x1000: -- return IPUV3_COLORSPACE_RGB; -- case 0x2000: -- return IPUV3_COLORSPACE_YUV; -- default: -- return IPUV3_COLORSPACE_UNKNOWN; -- } --} --EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -- --int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- case V4L2_PIX_FMT_YUV422P: -- case V4L2_PIX_FMT_NV12: -- case V4L2_PIX_FMT_NV21: -- case V4L2_PIX_FMT_NV16: -- case V4L2_PIX_FMT_NV61: -- /* -- * for the planar YUV formats, the stride passed to -- * cpmem must be the stride in bytes of the Y plane. -- * And all the planar YUV formats have an 8-bit -- * Y component. -- */ -- return (8 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_RGB565: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_UYVY: -- return (16 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR24: -- case V4L2_PIX_FMT_RGB24: -- return (24 * pixel_stride) >> 3; -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_XRGB32: -- return (32 * pixel_stride) >> 3; -- default: -- break; -- } -- -- return -EINVAL; --} --EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -- --int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- switch (degrees) { -- case 0: -- vf = hf = r90 = 0; -- break; -- case 90: -- vf = hf = 0; -- r90 = 1; -- break; -- case 180: -- vf = hf = 1; -- r90 = 0; -- break; -- case 270: -- vf = hf = r90 = 1; -- break; -- default: -- return -EINVAL; -- } -- -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -- --int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -- bool hflip, bool vflip) --{ -- u32 r90, vf, hf; -- -- r90 = ((u32)mode >> 2) & 0x1; -- hf = ((u32)mode >> 1) & 0x1; -- vf = ((u32)mode >> 0) & 0x1; -- hf ^= (u32)hflip; -- vf ^= (u32)vflip; -- -- switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -- case IPU_ROTATE_NONE: -- *degrees = 0; -- break; -- case IPU_ROTATE_90_RIGHT: -- *degrees = 90; -- break; -- case IPU_ROTATE_180: -- *degrees = 180; -- break; -- case IPU_ROTATE_90_LEFT: -- *degrees = 270; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -- --struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) --{ -- struct ipuv3_channel *channel; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, num); -- -- if (num > 63) -- return ERR_PTR(-ENODEV); -- -- mutex_lock(&ipu->channel_lock); -- -- list_for_each_entry(channel, &ipu->channels, list) { -- if (channel->num == num) { -- channel = ERR_PTR(-EBUSY); -- goto out; -- } -- } -- -- channel = kzalloc(sizeof(*channel), GFP_KERNEL); -- if (!channel) { -- channel = ERR_PTR(-ENOMEM); -- goto out; -- } -- -- channel->num = num; -- channel->ipu = ipu; -- list_add(&channel->list, &ipu->channels); -- --out: -- mutex_unlock(&ipu->channel_lock); -- -- return channel; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get); -- --void ipu_idmac_put(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- -- dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -- -- mutex_lock(&ipu->channel_lock); -- -- list_del(&channel->list); -- kfree(channel); -- -- mutex_unlock(&ipu->channel_lock); --} --EXPORT_SYMBOL_GPL(ipu_idmac_put); -- --#define idma_mask(ch) (1 << ((ch) & 0x1f)) -- --/* -- * This is an undocumented feature, a write one to a channel bit in -- * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -- * internal current buffer pointer so that transfers start from buffer -- * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -- * only says these are read-only registers). This operation is required -- * for channel linking to work correctly, for instance video capture -- * pipelines that carry out image rotations will fail after the first -- * streaming unless this function is called for each channel before -- * re-enabling the channels. -- */ --static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); --} -- --void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -- bool doublebuffer) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- if (doublebuffer) -- reg |= idma_mask(channel->num); -- else -- reg &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -- --static const struct { -- int chnum; -- u32 reg; -- int shift; --} idmac_lock_en_info[] = { -- { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -- { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -- { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -- { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -- { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -- { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -- { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -- { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -- { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -- { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -- { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -- { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -- { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -- { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -- { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -- { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -- { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, --}; -- --int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 bursts, regval; -- int i; -- -- switch (num_bursts) { -- case 0: -- case 1: -- bursts = 0x00; /* locking disabled */ -- break; -- case 2: -- bursts = 0x01; -- break; -- case 4: -- bursts = 0x02; -- break; -- case 8: -- bursts = 0x03; -- break; -- default: -- return -EINVAL; -- } -- -- /* -- * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -- * i.MX53 channel arbitration locking doesn't seem to work properly. -- * Allow enabling the lock feature on IPUv3H / i.MX6 only. -- */ -- if (bursts && ipu->ipu_type != IPUV3H) -- return -EINVAL; -- -- for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -- if (channel->num == idmac_lock_en_info[i].chnum) -- break; -- } -- if (i >= ARRAY_SIZE(idmac_lock_en_info)) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -- regval &= ~(0x03 << idmac_lock_en_info[i].shift); -- regval |= (bursts << idmac_lock_en_info[i].shift); -- ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val |= IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val |= IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val |= mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_enable); -- --int ipu_module_disable(struct ipu_soc *ipu, u32 mask) --{ -- unsigned long lock_flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, lock_flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- val = ipu_cm_read(ipu, IPU_DISP_GEN); -- -- if (mask & IPU_CONF_DI0_EN) -- val &= ~IPU_DI0_COUNTER_RELEASE; -- if (mask & IPU_CONF_DI1_EN) -- val &= ~IPU_DI1_COUNTER_RELEASE; -- -- ipu_cm_write(ipu, val, IPU_DISP_GEN); -- -- spin_unlock_irqrestore(&ipu->lock, lock_flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_module_disable); -- --int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- -- return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -- --bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 reg = 0; -- -- spin_lock_irqsave(&ipu->lock, flags); -- switch (buf_num) { -- case 0: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -- break; -- case 1: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -- break; -- case 2: -- reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -- break; -- } -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return ((reg & idma_mask(channel->num)) != 0); --} --EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -- --void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Mark buffer as ready. */ -- if (buf_num == 0) -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- else -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -- --void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned int chno = channel->num; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -- switch (buf_num) { -- case 0: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -- break; -- case 1: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -- break; -- case 2: -- ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -- break; -- default: -- break; -- } -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -- --int ipu_idmac_enable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val |= idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) --{ -- return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -- --int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long timeout; -- -- timeout = jiffies + msecs_to_jiffies(ms); -- while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -- idma_mask(channel->num)) { -- if (time_after(jiffies, timeout)) -- return -ETIMEDOUT; -- cpu_relax(); -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -- --int ipu_idmac_disable_channel(struct ipuv3_channel *channel) --{ -- struct ipu_soc *ipu = channel->ipu; -- u32 val; -- unsigned long flags; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- /* Disable DMA channel(s) */ -- val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -- -- __ipu_idmac_reset_current_buffer(channel); -- -- /* Set channel buffers NOT to be ready */ -- ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF0_RDY(channel->num)); -- } -- -- if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -- idma_mask(channel->num)) { -- ipu_cm_write(ipu, idma_mask(channel->num), -- IPU_CHA_BUF1_RDY(channel->num)); -- } -- -- ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -- -- /* Reset the double buffer */ -- val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -- val &= ~idma_mask(channel->num); -- ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -- --/* -- * The imx6 rev. D TRM says that enabling the WM feature will increase -- * a channel's priority. Refer to Table 36-8 Calculated priority value. -- * The sub-module that is the sink or source for the channel must enable -- * watermark signal for this to take effect (SMFC_WM for instance). -- */ --void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) --{ -- struct ipu_soc *ipu = channel->ipu; -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -- if (enable) -- val |= 1 << (channel->num % 32); -- else -- val &= ~(1 << (channel->num % 32)); -- ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -- --static int ipu_memory_reset(struct ipu_soc *ipu) --{ -- unsigned long timeout; -- -- ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -- -- timeout = jiffies + msecs_to_jiffies(1000); -- while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -- if (time_after(jiffies, timeout)) -- return -ETIME; -- cpu_relax(); -- } -- -- return 0; --} -- --/* -- * Set the source mux for the given CSI. Selects either parallel or -- * MIPI CSI2 sources. -- */ --void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) --{ -- unsigned long flags; -- u32 val, mask; -- -- mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -- IPU_CONF_CSI0_DATA_SOURCE; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (mipi_csi2) -- val |= mask; -- else -- val &= ~mask; -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -- --/* -- * Set the source mux for the IC. Selects either CSI[01] or the VDI. -- */ --void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) --{ -- unsigned long flags; -- u32 val; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- val = ipu_cm_read(ipu, IPU_CONF); -- if (vdi) -- val |= IPU_CONF_IC_INPUT; -- else -- val &= ~IPU_CONF_IC_INPUT; -- -- if (csi_id == 1) -- val |= IPU_CONF_CSI_SEL; -- else -- val &= ~IPU_CONF_CSI_SEL; -- -- ipu_cm_write(ipu, val, IPU_CONF); -- -- spin_unlock_irqrestore(&ipu->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -- -- --/* Frame Synchronization Unit Channel Linking */ -- --struct fsu_link_reg_info { -- int chno; -- u32 reg; -- u32 mask; -- u32 val; --}; -- --struct fsu_link_info { -- struct fsu_link_reg_info src; -- struct fsu_link_reg_info sink; --}; -- --static const struct fsu_link_info fsu_link_info[] = { -- { -- .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -- FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -- FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -- FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -- FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -- }, { -- .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -- FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -- .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -- FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -- }, { -- .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -- .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -- FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -- }, --}; -- --static const struct fsu_link_info *find_fsu_link_info(int src, int sink) --{ -- int i; -- -- for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -- if (src == fsu_link_info[i].src.chno && -- sink == fsu_link_info[i].sink.chno) -- return &fsu_link_info[i]; -- } -- -- return NULL; --} -- --/* -- * Links a source channel to a sink channel in the FSU. -- */ --int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- src_reg |= link->src.val; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- sink_reg |= link->sink.val; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_link); -- --/* -- * Unlinks source and sink channels in the FSU. -- */ --int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) --{ -- const struct fsu_link_info *link; -- u32 src_reg, sink_reg; -- unsigned long flags; -- -- link = find_fsu_link_info(src_ch, sink_ch); -- if (!link) -- return -EINVAL; -- -- spin_lock_irqsave(&ipu->lock, flags); -- -- if (link->src.mask) { -- src_reg = ipu_cm_read(ipu, link->src.reg); -- src_reg &= ~link->src.mask; -- ipu_cm_write(ipu, src_reg, link->src.reg); -- } -- -- if (link->sink.mask) { -- sink_reg = ipu_cm_read(ipu, link->sink.reg); -- sink_reg &= ~link->sink.mask; -- ipu_cm_write(ipu, sink_reg, link->sink.reg); -- } -- -- spin_unlock_irqrestore(&ipu->lock, flags); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -- --/* Link IDMAC channels in the FSU */ --int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_link(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_link); -- --/* Unlink IDMAC channels in the FSU */ --int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) --{ -- return ipu_fsu_unlink(src->ipu, src->num, sink->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -- --struct ipu_devtype { -- const char *name; -- unsigned long cm_ofs; -- unsigned long cpmem_ofs; -- unsigned long srm_ofs; -- unsigned long tpm_ofs; -- unsigned long csi0_ofs; -- unsigned long csi1_ofs; -- unsigned long ic_ofs; -- unsigned long disp0_ofs; -- unsigned long disp1_ofs; -- unsigned long dc_tmpl_ofs; -- unsigned long vdi_ofs; -- enum ipuv3_type type; --}; -- --static struct ipu_devtype ipu_type_imx51 = { -- .name = "IPUv3EX", -- .cm_ofs = 0x1e000000, -- .cpmem_ofs = 0x1f000000, -- .srm_ofs = 0x1f040000, -- .tpm_ofs = 0x1f060000, -- .csi0_ofs = 0x1e030000, -- .csi1_ofs = 0x1e038000, -- .ic_ofs = 0x1e020000, -- .disp0_ofs = 0x1e040000, -- .disp1_ofs = 0x1e048000, -- .dc_tmpl_ofs = 0x1f080000, -- .vdi_ofs = 0x1e068000, -- .type = IPUV3EX, --}; -- --static struct ipu_devtype ipu_type_imx53 = { -- .name = "IPUv3M", -- .cm_ofs = 0x06000000, -- .cpmem_ofs = 0x07000000, -- .srm_ofs = 0x07040000, -- .tpm_ofs = 0x07060000, -- .csi0_ofs = 0x06030000, -- .csi1_ofs = 0x06038000, -- .ic_ofs = 0x06020000, -- .disp0_ofs = 0x06040000, -- .disp1_ofs = 0x06048000, -- .dc_tmpl_ofs = 0x07080000, -- .vdi_ofs = 0x06068000, -- .type = IPUV3M, --}; -- --static struct ipu_devtype ipu_type_imx6q = { -- .name = "IPUv3H", -- .cm_ofs = 0x00200000, -- .cpmem_ofs = 0x00300000, -- .srm_ofs = 0x00340000, -- .tpm_ofs = 0x00360000, -- .csi0_ofs = 0x00230000, -- .csi1_ofs = 0x00238000, -- .ic_ofs = 0x00220000, -- .disp0_ofs = 0x00240000, -- .disp1_ofs = 0x00248000, -- .dc_tmpl_ofs = 0x00380000, -- .vdi_ofs = 0x00268000, -- .type = IPUV3H, --}; -- --static const struct of_device_id imx_ipu_dt_ids[] = { -- { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -- { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -- { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -- { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -- { /* sentinel */ } --}; --MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -- --static int ipu_submodules_init(struct ipu_soc *ipu, -- struct platform_device *pdev, unsigned long ipu_base, -- struct clk *ipu_clk) --{ -- char *unit; -- int ret; -- struct device *dev = &pdev->dev; -- const struct ipu_devtype *devtype = ipu->devtype; -- -- ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -- if (ret) { -- unit = "cpmem"; -- goto err_cpmem; -- } -- -- ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -- IPU_CONF_CSI0_EN, ipu_clk); -- if (ret) { -- unit = "csi0"; -- goto err_csi_0; -- } -- -- ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -- IPU_CONF_CSI1_EN, ipu_clk); -- if (ret) { -- unit = "csi1"; -- goto err_csi_1; -- } -- -- ret = ipu_ic_init(ipu, dev, -- ipu_base + devtype->ic_ofs, -- ipu_base + devtype->tpm_ofs); -- if (ret) { -- unit = "ic"; -- goto err_ic; -- } -- -- ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -- IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -- IPU_CONF_IC_INPUT); -- if (ret) { -- unit = "vdi"; -- goto err_vdi; -- } -- -- ret = ipu_image_convert_init(ipu, dev); -- if (ret) { -- unit = "image_convert"; -- goto err_image_convert; -- } -- -- ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -- IPU_CONF_DI0_EN, ipu_clk); -- if (ret) { -- unit = "di0"; -- goto err_di_0; -- } -- -- ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -- IPU_CONF_DI1_EN, ipu_clk); -- if (ret) { -- unit = "di1"; -- goto err_di_1; -- } -- -- ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -- IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -- if (ret) { -- unit = "dc_template"; -- goto err_dc; -- } -- -- ret = ipu_dmfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -- if (ret) { -- unit = "dmfc"; -- goto err_dmfc; -- } -- -- ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -- if (ret) { -- unit = "dp"; -- goto err_dp; -- } -- -- ret = ipu_smfc_init(ipu, dev, ipu_base + -- devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -- if (ret) { -- unit = "smfc"; -- goto err_smfc; -- } -- -- return 0; -- --err_smfc: -- ipu_dp_exit(ipu); --err_dp: -- ipu_dmfc_exit(ipu); --err_dmfc: -- ipu_dc_exit(ipu); --err_dc: -- ipu_di_exit(ipu, 1); --err_di_1: -- ipu_di_exit(ipu, 0); --err_di_0: -- ipu_image_convert_exit(ipu); --err_image_convert: -- ipu_vdi_exit(ipu); --err_vdi: -- ipu_ic_exit(ipu); --err_ic: -- ipu_csi_exit(ipu, 1); --err_csi_1: -- ipu_csi_exit(ipu, 0); --err_csi_0: -- ipu_cpmem_exit(ipu); --err_cpmem: -- dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -- return ret; --} -- --static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) --{ -- unsigned long status; -- int i, bit, irq; -- -- for (i = 0; i < num_regs; i++) { -- -- status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -- status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -- -- for_each_set_bit(bit, &status, 32) { -- irq = irq_linear_revmap(ipu->domain, -- regs[i] * 32 + bit); -- if (irq) -- generic_handle_irq(irq); -- } -- } --} -- --static void ipu_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --static void ipu_err_irq_handler(struct irq_desc *desc) --{ -- struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -- struct irq_chip *chip = irq_desc_get_chip(desc); -- static const int int_reg[] = { 4, 5, 8, 9}; -- -- chained_irq_enter(chip, desc); -- -- ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -- -- chained_irq_exit(chip, desc); --} -- --int ipu_map_irq(struct ipu_soc *ipu, int irq) --{ -- int virq; -- -- virq = irq_linear_revmap(ipu->domain, irq); -- if (!virq) -- virq = irq_create_mapping(ipu->domain, irq); -- -- return virq; --} --EXPORT_SYMBOL_GPL(ipu_map_irq); -- --int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -- enum ipu_channel_irq irq_type) --{ -- return ipu_map_irq(ipu, irq_type + channel->num); --} --EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -- --static void ipu_submodules_exit(struct ipu_soc *ipu) --{ -- ipu_smfc_exit(ipu); -- ipu_dp_exit(ipu); -- ipu_dmfc_exit(ipu); -- ipu_dc_exit(ipu); -- ipu_di_exit(ipu, 1); -- ipu_di_exit(ipu, 0); -- ipu_image_convert_exit(ipu); -- ipu_vdi_exit(ipu); -- ipu_ic_exit(ipu); -- ipu_csi_exit(ipu, 1); -- ipu_csi_exit(ipu, 0); -- ipu_cpmem_exit(ipu); --} -- --static int platform_remove_devices_fn(struct device *dev, void *unused) --{ -- struct platform_device *pdev = to_platform_device(dev); -- -- platform_device_unregister(pdev); -- -- return 0; --} -- --static void platform_device_unregister_children(struct platform_device *pdev) --{ -- device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); --} -- --struct ipu_platform_reg { -- struct ipu_client_platformdata pdata; -- const char *name; --}; -- --/* These must be in the order of the corresponding device tree port nodes */ --static struct ipu_platform_reg client_reg[] = { -- { -- .pdata = { -- .csi = 0, -- .dma[0] = IPUV3_CHANNEL_CSI0, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .csi = 1, -- .dma[0] = IPUV3_CHANNEL_CSI1, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-csi", -- }, { -- .pdata = { -- .di = 0, -- .dc = 5, -- .dp = IPU_DP_FLOW_SYNC_BG, -- .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -- .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -- }, -- .name = "imx-ipuv3-crtc", -- }, { -- .pdata = { -- .di = 1, -- .dc = 1, -- .dp = -EINVAL, -- .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -- .dma[1] = -EINVAL, -- }, -- .name = "imx-ipuv3-crtc", -- }, --}; -- --static DEFINE_MUTEX(ipu_client_id_mutex); --static int ipu_client_id; -- --static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) --{ -- struct device *dev = ipu->dev; -- unsigned i; -- int id, ret; -- -- mutex_lock(&ipu_client_id_mutex); -- id = ipu_client_id; -- ipu_client_id += ARRAY_SIZE(client_reg); -- mutex_unlock(&ipu_client_id_mutex); -- -- for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -- struct ipu_platform_reg *reg = &client_reg[i]; -- struct platform_device *pdev; -- struct device_node *of_node; -- -- /* Associate subdevice with the corresponding port node */ -- of_node = of_graph_get_port_by_id(dev->of_node, i); -- if (!of_node) { -- dev_info(dev, -- "no port@%d node in %pOF, not using %s%d\n", -- i, dev->of_node, -- (i / 2) ? "DI" : "CSI", i % 2); -- continue; -- } -- -- pdev = platform_device_alloc(reg->name, id++); -- if (!pdev) { -- ret = -ENOMEM; -- goto err_register; -- } -- -- pdev->dev.parent = dev; -- -- reg->pdata.of_node = of_node; -- ret = platform_device_add_data(pdev, ®->pdata, -- sizeof(reg->pdata)); -- if (!ret) -- ret = platform_device_add(pdev); -- if (ret) { -- platform_device_put(pdev); -- goto err_register; -- } -- } -- -- return 0; -- --err_register: -- platform_device_unregister_children(to_platform_device(dev)); -- -- return ret; --} -- -- --static int ipu_irq_init(struct ipu_soc *ipu) --{ -- struct irq_chip_generic *gc; -- struct irq_chip_type *ct; -- unsigned long unused[IPU_NUM_IRQS / 32] = { -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x400100d0, 0xffe000fd, -- 0x4077ffff, 0xffe7e1fd, -- 0x23fffffe, 0x8880fff0, -- 0xf98fe7d0, 0xfff81fff, -- 0x400100d0, 0xffe000fd, -- 0x00000000, -- }; -- int ret, i; -- -- ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -- &irq_generic_chip_ops, ipu); -- if (!ipu->domain) { -- dev_err(ipu->dev, "failed to add irq domain\n"); -- return -ENODEV; -- } -- -- ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -- handle_level_irq, 0, 0, 0); -- if (ret < 0) { -- dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -- irq_domain_remove(ipu->domain); -- return ret; -- } -- -- /* Mask and clear all interrupts */ -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -- ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -- } -- -- for (i = 0; i < IPU_NUM_IRQS; i += 32) { -- gc = irq_get_domain_generic_chip(ipu->domain, i); -- gc->reg_base = ipu->cm_reg; -- gc->unused = unused[i / 32]; -- ct = gc->chip_types; -- ct->chip.irq_ack = irq_gc_ack_set_bit; -- ct->chip.irq_mask = irq_gc_mask_clr_bit; -- ct->chip.irq_unmask = irq_gc_mask_set_bit; -- ct->regs.ack = IPU_INT_STAT(i / 32); -- ct->regs.mask = IPU_INT_CTRL(i / 32); -- } -- -- irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -- irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -- ipu); -- -- return 0; --} -- --static void ipu_irq_exit(struct ipu_soc *ipu) --{ -- int i, irq; -- -- irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -- irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -- -- /* TODO: remove irq_domain_generic_chips */ -- -- for (i = 0; i < IPU_NUM_IRQS; i++) { -- irq = irq_linear_revmap(ipu->domain, i); -- if (irq) -- irq_dispose_mapping(irq); -- } -- -- irq_domain_remove(ipu->domain); --} -- --void ipu_dump(struct ipu_soc *ipu) --{ -- int i; -- -- dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CONF)); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -- dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -- dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -- ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -- dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -- dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -- dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -- ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -- for (i = 0; i < 15; i++) -- dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -- ipu_cm_read(ipu, IPU_INT_CTRL(i))); --} --EXPORT_SYMBOL_GPL(ipu_dump); -- --static int ipu_probe(struct platform_device *pdev) --{ -- struct device_node *np = pdev->dev.of_node; -- struct ipu_soc *ipu; -- struct resource *res; -- unsigned long ipu_base; -- int ret, irq_sync, irq_err; -- const struct ipu_devtype *devtype; -- -- devtype = of_device_get_match_data(&pdev->dev); -- if (!devtype) -- return -EINVAL; -- -- irq_sync = platform_get_irq(pdev, 0); -- irq_err = platform_get_irq(pdev, 1); -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- -- dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -- irq_sync, irq_err); -- -- if (!res || irq_sync < 0 || irq_err < 0) -- return -ENODEV; -- -- ipu_base = res->start; -- -- ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -- if (!ipu) -- return -ENODEV; -- -- ipu->id = of_alias_get_id(np, "ipu"); -- if (ipu->id < 0) -- ipu->id = 0; -- -- if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -- IS_ENABLED(CONFIG_DRM)) { -- ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -- "fsl,prg", ipu->id); -- if (!ipu->prg_priv) -- return -EPROBE_DEFER; -- } -- -- ipu->devtype = devtype; -- ipu->ipu_type = devtype->type; -- -- spin_lock_init(&ipu->lock); -- mutex_init(&ipu->channel_lock); -- INIT_LIST_HEAD(&ipu->channels); -- -- dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -- ipu_base + devtype->cm_ofs); -- dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -- dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -- ipu_base + devtype->cpmem_ofs); -- dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -- ipu_base + devtype->csi0_ofs); -- dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -- ipu_base + devtype->csi1_ofs); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->ic_ofs); -- dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -- ipu_base + devtype->disp0_ofs); -- dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -- ipu_base + devtype->disp1_ofs); -- dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -- ipu_base + devtype->srm_ofs); -- dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -- ipu_base + devtype->tpm_ofs); -- dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -- dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -- dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -- ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -- dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -- ipu_base + devtype->vdi_ofs); -- -- ipu->cm_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs, PAGE_SIZE); -- ipu->idmac_reg = devm_ioremap(&pdev->dev, -- ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -- PAGE_SIZE); -- -- if (!ipu->cm_reg || !ipu->idmac_reg) -- return -ENOMEM; -- -- ipu->clk = devm_clk_get(&pdev->dev, "bus"); -- if (IS_ERR(ipu->clk)) { -- ret = PTR_ERR(ipu->clk); -- dev_err(&pdev->dev, "clk_get failed with %d", ret); -- return ret; -- } -- -- platform_set_drvdata(pdev, ipu); -- -- ret = clk_prepare_enable(ipu->clk); -- if (ret) { -- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -- return ret; -- } -- -- ipu->dev = &pdev->dev; -- ipu->irq_sync = irq_sync; -- ipu->irq_err = irq_err; -- -- ret = device_reset(&pdev->dev); -- if (ret) { -- dev_err(&pdev->dev, "failed to reset: %d\n", ret); -- goto out_failed_reset; -- } -- ret = ipu_memory_reset(ipu); -- if (ret) -- goto out_failed_reset; -- -- ret = ipu_irq_init(ipu); -- if (ret) -- goto out_failed_irq; -- -- /* Set MCU_T to divide MCU access window into 2 */ -- ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -- IPU_DISP_GEN); -- -- ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -- if (ret) -- goto failed_submodules_init; -- -- ret = ipu_add_client_devices(ipu, ipu_base); -- if (ret) { -- dev_err(&pdev->dev, "adding client devices failed with %d\n", -- ret); -- goto failed_add_clients; -- } -- -- dev_info(&pdev->dev, "%s probed\n", devtype->name); -- -- return 0; -- --failed_add_clients: -- ipu_submodules_exit(ipu); --failed_submodules_init: -- ipu_irq_exit(ipu); --out_failed_irq: --out_failed_reset: -- clk_disable_unprepare(ipu->clk); -- return ret; --} -- --static int ipu_remove(struct platform_device *pdev) --{ -- struct ipu_soc *ipu = platform_get_drvdata(pdev); -- -- platform_device_unregister_children(pdev); -- ipu_submodules_exit(ipu); -- ipu_irq_exit(ipu); -- -- clk_disable_unprepare(ipu->clk); -- -- return 0; --} -- --static struct platform_driver imx_ipu_driver = { -- .driver = { -- .name = "imx-ipuv3", -- .of_match_table = imx_ipu_dt_ids, -- }, -- .probe = ipu_probe, -- .remove = ipu_remove, --}; -- --static struct platform_driver * const drivers[] = { --#if IS_ENABLED(CONFIG_DRM) -- &ipu_pre_drv, -- &ipu_prg_drv, --#endif -- &imx_ipu_driver, --}; -- --static int __init imx_ipu_init(void) --{ -- return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_init(imx_ipu_init); -- --static void __exit imx_ipu_exit(void) --{ -- platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); --} --module_exit(imx_ipu_exit); -- --MODULE_ALIAS("platform:imx-ipuv3"); --MODULE_DESCRIPTION("i.MX IPU v3 driver"); --MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); --MODULE_LICENSE("GPL"); ---- a/drivers/gpu/imx/ipu-v3/ipu-cpmem.c -+++ /dev/null -@@ -1,976 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#include <linux/types.h> --#include <linux/bitrev.h> --#include <linux/io.h> --#include <linux/sizes.h> --#include <drm/drm_fourcc.h> --#include "ipu-prv.h" -- --struct ipu_cpmem_word { -- u32 data[5]; -- u32 res[3]; --}; -- --struct ipu_ch_param { -- struct ipu_cpmem_word word[2]; --}; -- --struct ipu_cpmem { -- struct ipu_ch_param __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- --#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -- --#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) --#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) --#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) --#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) --#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) --#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -- --#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) --#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) --#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) --#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) --#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) --#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) --#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) --#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) --#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) --#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) --#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) --#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) --#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) --#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) --#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) --#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) --#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) --#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) --#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) --#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) --#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) --#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) --#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) --#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) --#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) --#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) --#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) --#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) --#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) --#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) --#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) --#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) --#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) --#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) --#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) --#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) --#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) --#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) --#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) --#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) --#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) --#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) --#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) --#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) --#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) --#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) --#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) --#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) --#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) --#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) --#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -- --static inline struct ipu_ch_param __iomem * --ipu_get_cpmem(struct ipuv3_channel *ch) --{ -- struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -- -- return cpmem->base + ch->num; --} -- --static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = readl(&base->word[word].data[i]); -- val &= ~(mask << ofs); -- val |= v << ofs; -- writel(val, &base->word[word].data[i]); -- -- if ((bit + size - 1) / 32 > i) { -- val = readl(&base->word[word].data[i + 1]); -- val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -- val |= v >> (ofs ? (32 - ofs) : 0); -- writel(val, &base->word[word].data[i + 1]); -- } --} -- --static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) --{ -- struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -- u32 bit = (wbs >> 8) % 160; -- u32 size = wbs & 0xff; -- u32 word = (wbs >> 8) / 160; -- u32 i = bit / 32; -- u32 ofs = bit % 32; -- u32 mask = (1 << size) - 1; -- u32 val = 0; -- -- pr_debug("%s %d %d %d\n", __func__, word, bit , size); -- -- val = (readl(&base->word[word].data[i]) >> ofs) & mask; -- -- if ((bit + size - 1) / 32 > i) { -- u32 tmp; -- -- tmp = readl(&base->word[word].data[i + 1]); -- tmp &= mask >> (ofs ? (32 - ofs) : 0); -- val |= tmp << (ofs ? (32 - ofs) : 0); -- } -- -- return val; --} -- --/* -- * The V4L2 spec defines packed RGB formats in memory byte order, which from -- * point of view of the IPU corresponds to little-endian words with the first -- * component in the least significant bits. -- * The DRM pixel formats and IPU internal representation are ordered the other -- * way around, with the first named component ordered at the most significant -- * bits. Further, V4L2 formats are not well defined: -- * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -- * We choose the interpretation which matches GStreamer behavior. -- */ --static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) --{ -- switch (pixelformat) { -- case V4L2_PIX_FMT_RGB565: -- /* -- * Here we choose the 'corrected' interpretation of RGBP, a -- * little-endian 16-bit word with the red component at the most -- * significant bits: -- * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -- */ -- return DRM_FORMAT_RGB565; -- case V4L2_PIX_FMT_BGR24: -- /* B G R <=> [24:0] R:G:B */ -- return DRM_FORMAT_RGB888; -- case V4L2_PIX_FMT_RGB24: -- /* R G B <=> [24:0] B:G:R */ -- return DRM_FORMAT_BGR888; -- case V4L2_PIX_FMT_BGR32: -- /* B G R A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_RGB32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ABGR32: -- /* B G R A <=> [32:0] A:R:G:B */ -- return DRM_FORMAT_ARGB8888; -- case V4L2_PIX_FMT_XBGR32: -- /* B G R X <=> [32:0] X:R:G:B */ -- return DRM_FORMAT_XRGB8888; -- case V4L2_PIX_FMT_BGRA32: -- /* A B G R <=> [32:0] R:G:B:A */ -- return DRM_FORMAT_RGBA8888; -- case V4L2_PIX_FMT_BGRX32: -- /* X B G R <=> [32:0] R:G:B:X */ -- return DRM_FORMAT_RGBX8888; -- case V4L2_PIX_FMT_RGBA32: -- /* R G B A <=> [32:0] A:B:G:R */ -- return DRM_FORMAT_ABGR8888; -- case V4L2_PIX_FMT_RGBX32: -- /* R G B X <=> [32:0] X:B:G:R */ -- return DRM_FORMAT_XBGR8888; -- case V4L2_PIX_FMT_ARGB32: -- /* A R G B <=> [32:0] B:G:R:A */ -- return DRM_FORMAT_BGRA8888; -- case V4L2_PIX_FMT_XRGB32: -- /* X R G B <=> [32:0] B:G:R:X */ -- return DRM_FORMAT_BGRX8888; -- case V4L2_PIX_FMT_UYVY: -- return DRM_FORMAT_UYVY; -- case V4L2_PIX_FMT_YUYV: -- return DRM_FORMAT_YUYV; -- case V4L2_PIX_FMT_YUV420: -- return DRM_FORMAT_YUV420; -- case V4L2_PIX_FMT_YUV422P: -- return DRM_FORMAT_YUV422; -- case V4L2_PIX_FMT_YVU420: -- return DRM_FORMAT_YVU420; -- case V4L2_PIX_FMT_NV12: -- return DRM_FORMAT_NV12; -- case V4L2_PIX_FMT_NV16: -- return DRM_FORMAT_NV16; -- } -- -- return -EINVAL; --} -- --void ipu_cpmem_zero(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- void __iomem *base = p; -- int i; -- -- for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -- writel(0, base + i * sizeof(u32)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -- --void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -- --void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -- --void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -- --void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- u32 val; -- -- if (ipu->ipu_type == IPUV3EX) -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -- val |= 1 << (ch->num % 32); -- ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -- --void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) --{ -- WARN_ON_ONCE(buf & 0x7); -- -- if (bufnum) -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -- else -- ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -- --void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) --{ -- WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -- --void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -- u32 pixelformat) --{ -- u32 ilo, sly, sluv; -- -- if (stride < 0) { -- stride = -stride; -- ilo = 0x100000 - (stride / 8); -- } else { -- ilo = stride / 8; -- } -- -- sly = (stride * 2) - 1; -- -- switch (pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- case V4L2_PIX_FMT_YVU420: -- sluv = stride / 2 - 1; -- break; -- case V4L2_PIX_FMT_NV12: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_YUV422P: -- sluv = stride - 1; -- break; -- case V4L2_PIX_FMT_NV16: -- sluv = stride * 2 - 1; -- break; -- default: -- sluv = 0; -- break; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -- ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -- if (sluv) -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -- --void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) --{ -- id &= 0x3; -- ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -- --int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) --{ -- return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -- --void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); --}; --EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -- --void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) --{ -- ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -- --void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -- enum ipu_rotate_mode rot) --{ -- u32 temp_rot = bitrev8(rot) >> 5; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -- --int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -- const struct ipu_rgb *rgb) --{ -- int bpp = 0, npb = 0, ro, go, bo, to; -- -- ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -- go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -- bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -- to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -- ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -- -- if (rgb->transp.length) { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -- rgb->transp.length - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -- } else { -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -- rgb->bits_per_pixel); -- } -- -- switch (rgb->bits_per_pixel) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -- --int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) --{ -- int bpp = 0, npb = 0; -- -- switch (width) { -- case 32: -- bpp = 0; -- npb = 15; -- break; -- case 24: -- bpp = 1; -- npb = 19; -- break; -- case 16: -- bpp = 3; -- npb = 31; -- break; -- case 8: -- bpp = 5; -- npb = 63; -- break; -- default: -- return -EINVAL; -- } -- -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -- --void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) --{ -- switch (pixel_format) { -- case V4L2_PIX_FMT_UYVY: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- case V4L2_PIX_FMT_YUYV: -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -- break; -- } --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -- --void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -- unsigned int uv_stride, -- unsigned int u_offset, unsigned int v_offset) --{ -- WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -- -- ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -- ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -- --static const struct ipu_rgb def_xrgb_32 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_xbgr_32 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 24, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgbx_32 = { -- .red = { .offset = 24, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 8, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_bgrx_32 = { -- .red = { .offset = 8, .length = 8, }, -- .green = { .offset = 16, .length = 8, }, -- .blue = { .offset = 24, .length = 8, }, -- .transp = { .offset = 0, .length = 8, }, -- .bits_per_pixel = 32, --}; -- --static const struct ipu_rgb def_rgb_24 = { -- .red = { .offset = 16, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 0, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_bgr_24 = { -- .red = { .offset = 0, .length = 8, }, -- .green = { .offset = 8, .length = 8, }, -- .blue = { .offset = 16, .length = 8, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 24, --}; -- --static const struct ipu_rgb def_rgb_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 6, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 0, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16 = { -- .red = { .offset = 10, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 0, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_argb_16_4444 = { -- .red = { .offset = 8, .length = 4, }, -- .green = { .offset = 4, .length = 4, }, -- .blue = { .offset = 0, .length = 4, }, -- .transp = { .offset = 12, .length = 4, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_abgr_16 = { -- .red = { .offset = 0, .length = 5, }, -- .green = { .offset = 5, .length = 5, }, -- .blue = { .offset = 10, .length = 5, }, -- .transp = { .offset = 15, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_rgba_16 = { -- .red = { .offset = 11, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 1, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --static const struct ipu_rgb def_bgra_16 = { -- .red = { .offset = 1, .length = 5, }, -- .green = { .offset = 6, .length = 5, }, -- .blue = { .offset = 11, .length = 5, }, -- .transp = { .offset = 0, .length = 1, }, -- .bits_per_pixel = 16, --}; -- --#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) --#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 4) + \ -- (pix->width * ((y) / 2) / 2) + (x) / 2) --#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * pix->height / 2) + \ -- (pix->width * (y) / 2) + (x) / 2) --#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * ((y) / 2)) + (x)) --#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -- (pix->width * y) + (x)) -- --#define NUM_ALPHA_CHANNELS 7 -- --/* See Table 37-12. Alpha channels mapping. */ --static int ipu_channel_albm(int ch_num) --{ -- switch (ch_num) { -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -- case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -- case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -- case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -- case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -- case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -- case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -- default: -- return -EINVAL; -- } --} -- --static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) --{ -- struct ipu_soc *ipu = ch->ipu; -- int albm; -- u32 val; -- -- albm = ipu_channel_albm(ch->num); -- if (albm < 0) -- return; -- -- ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -- ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -- ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -- -- val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -- val |= BIT(ch->num); -- ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); --} -- --int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) --{ -- switch (drm_fourcc) { -- case DRM_FORMAT_YUV420: -- case DRM_FORMAT_YVU420: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV422: -- case DRM_FORMAT_YVU422: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUV444: -- case DRM_FORMAT_YVU444: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV12: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_NV16: -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_UYVY: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_YUYV: -- /* bits/pixel */ -- ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -- /* pix format */ -- ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -- /* burst size */ -- ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -- break; -- case DRM_FORMAT_ABGR8888: -- case DRM_FORMAT_XBGR8888: -- ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -- break; -- case DRM_FORMAT_ARGB8888: -- case DRM_FORMAT_XRGB8888: -- ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -- break; -- case DRM_FORMAT_RGBA8888: -- case DRM_FORMAT_RGBX8888: -- case DRM_FORMAT_RGBX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -- break; -- case DRM_FORMAT_BGRA8888: -- case DRM_FORMAT_BGRX8888: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -- break; -- case DRM_FORMAT_BGR888: -- case DRM_FORMAT_BGR888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -- break; -- case DRM_FORMAT_RGB888: -- case DRM_FORMAT_RGB888_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -- break; -- case DRM_FORMAT_RGB565: -- case DRM_FORMAT_RGB565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -- break; -- case DRM_FORMAT_BGR565: -- case DRM_FORMAT_BGR565_A8: -- ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -- break; -- case DRM_FORMAT_ARGB1555: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16); -- break; -- case DRM_FORMAT_ABGR1555: -- ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -- break; -- case DRM_FORMAT_RGBA5551: -- ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -- break; -- case DRM_FORMAT_BGRA5551: -- ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -- break; -- case DRM_FORMAT_ARGB4444: -- ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -- break; -- default: -- return -EINVAL; -- } -- -- switch (drm_fourcc) { -- case DRM_FORMAT_RGB565_A8: -- case DRM_FORMAT_BGR565_A8: -- case DRM_FORMAT_RGB888_A8: -- case DRM_FORMAT_BGR888_A8: -- case DRM_FORMAT_RGBX8888_A8: -- case DRM_FORMAT_BGRX8888_A8: -- ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -- ipu_cpmem_set_separate_alpha(ch); -- break; -- default: -- break; -- } -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -- --int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) --{ -- struct v4l2_pix_format *pix = &image->pix; -- int offset, u_offset, v_offset; -- int ret = 0; -- -- pr_debug("%s: resolution: %dx%d stride: %d\n", -- __func__, pix->width, pix->height, -- pix->bytesperline); -- -- ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -- ipu_cpmem_set_stride(ch, pix->bytesperline); -- -- ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -- -- switch (pix->pixelformat) { -- case V4L2_PIX_FMT_YUV420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YVU420: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : V_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : U_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_YUV422P: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : U2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? -- image->v_offset : V2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV12: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_NV16: -- offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -- u_offset = image->u_offset ? -- image->u_offset : UV2_OFFSET(pix, image->rect.left, -- image->rect.top) - offset; -- v_offset = image->v_offset ? image->v_offset : 0; -- -- ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -- u_offset, v_offset); -- break; -- case V4L2_PIX_FMT_UYVY: -- case V4L2_PIX_FMT_YUYV: -- case V4L2_PIX_FMT_RGB565: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB32: -- case V4L2_PIX_FMT_BGR32: -- case V4L2_PIX_FMT_ABGR32: -- case V4L2_PIX_FMT_XBGR32: -- case V4L2_PIX_FMT_BGRA32: -- case V4L2_PIX_FMT_BGRX32: -- case V4L2_PIX_FMT_RGBA32: -- case V4L2_PIX_FMT_RGBX32: -- case V4L2_PIX_FMT_ARGB32: -- case V4L2_PIX_FMT_XRGB32: -- offset = image->rect.left * 4 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_RGB24: -- case V4L2_PIX_FMT_BGR24: -- offset = image->rect.left * 3 + -- image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR8: -- case V4L2_PIX_FMT_SGBRG8: -- case V4L2_PIX_FMT_SGRBG8: -- case V4L2_PIX_FMT_SRGGB8: -- case V4L2_PIX_FMT_GREY: -- offset = image->rect.left + image->rect.top * pix->bytesperline; -- break; -- case V4L2_PIX_FMT_SBGGR16: -- case V4L2_PIX_FMT_SGBRG16: -- case V4L2_PIX_FMT_SGRBG16: -- case V4L2_PIX_FMT_SRGGB16: -- case V4L2_PIX_FMT_Y16: -- offset = image->rect.left * 2 + -- image->rect.top * pix->bytesperline; -- break; -- default: -- /* This should not happen */ -- WARN_ON(1); -- offset = 0; -- ret = -EINVAL; -- } -- -- ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -- ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -- --void ipu_cpmem_dump(struct ipuv3_channel *ch) --{ -- struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -- struct ipu_soc *ipu = ch->ipu; -- int chno = ch->num; -- -- dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[0].data[0]), -- readl(&p->word[0].data[1]), -- readl(&p->word[0].data[2]), -- readl(&p->word[0].data[3]), -- readl(&p->word[0].data[4])); -- dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -- readl(&p->word[1].data[0]), -- readl(&p->word[1].data[1]), -- readl(&p->word[1].data[2]), -- readl(&p->word[1].data[3]), -- readl(&p->word[1].data[4])); -- dev_dbg(ipu->dev, "PFS 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -- dev_dbg(ipu->dev, "BPP 0x%x, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -- dev_dbg(ipu->dev, "NPB 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -- -- dev_dbg(ipu->dev, "FW %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -- dev_dbg(ipu->dev, "FH %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -- dev_dbg(ipu->dev, "EBA0 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -- dev_dbg(ipu->dev, "EBA1 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -- dev_dbg(ipu->dev, "Stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -- dev_dbg(ipu->dev, "scan_order %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -- dev_dbg(ipu->dev, "uv_stride %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -- dev_dbg(ipu->dev, "u_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -- dev_dbg(ipu->dev, "v_offset 0x%x\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -- -- dev_dbg(ipu->dev, "Width0 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -- dev_dbg(ipu->dev, "Width1 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -- dev_dbg(ipu->dev, "Width2 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -- dev_dbg(ipu->dev, "Width3 %d+1, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -- dev_dbg(ipu->dev, "Offset0 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -- dev_dbg(ipu->dev, "Offset1 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -- dev_dbg(ipu->dev, "Offset2 %d, ", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -- dev_dbg(ipu->dev, "Offset3 %d\n", -- ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); --} --EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_cpmem *cpmem; -- -- cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -- if (!cpmem) -- return -ENOMEM; -- -- ipu->cpmem_priv = cpmem; -- -- spin_lock_init(&cpmem->lock); -- cpmem->base = devm_ioremap(dev, base, SZ_128K); -- if (!cpmem->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -- base, cpmem->base); -- cpmem->ipu = ipu; -- -- return 0; --} -- --void ipu_cpmem_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-csi.c -+++ /dev/null -@@ -1,821 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> --#include <linux/videodev2.h> --#include <uapi/linux/v4l2-mediabus.h> --#include <linux/clk.h> --#include <linux/clk-provider.h> --#include <linux/clkdev.h> -- --#include "ipu-prv.h" -- --struct ipu_csi { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_ipu; /* IPU bus clock */ -- spinlock_t lock; -- bool inuse; -- struct ipu_soc *ipu; --}; -- --/* CSI Register Offsets */ --#define CSI_SENS_CONF 0x0000 --#define CSI_SENS_FRM_SIZE 0x0004 --#define CSI_ACT_FRM_SIZE 0x0008 --#define CSI_OUT_FRM_CTRL 0x000c --#define CSI_TST_CTRL 0x0010 --#define CSI_CCIR_CODE_1 0x0014 --#define CSI_CCIR_CODE_2 0x0018 --#define CSI_CCIR_CODE_3 0x001c --#define CSI_MIPI_DI 0x0020 --#define CSI_SKIP 0x0024 --#define CSI_CPD_CTRL 0x0028 --#define CSI_CPD_RC(n) (0x002c + ((n)*4)) --#define CSI_CPD_RS(n) (0x004c + ((n)*4)) --#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) --#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) --#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) --#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) --#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) --#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) --#define CSI_CPD_OFFSET1 0x00ec --#define CSI_CPD_OFFSET2 0x00f0 -- --/* CSI Register Fields */ --#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 --#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 --#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L --#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L --#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L --#define CSI_SENS_CONF_DATA_FMT_BAYER 3L --#define CSI_SENS_CONF_DATA_FMT_RGB565 4L --#define CSI_SENS_CONF_DATA_FMT_RGB555 5L --#define CSI_SENS_CONF_DATA_FMT_RGB444 6L --#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -- --#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 --#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 --#define CSI_SENS_CONF_DATA_POL_SHIFT 2 --#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 --#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 --#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 --#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 --#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 --#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 --#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -- --#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 --#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 --#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 --#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 --#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 --#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 --#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -- --#define CSI_DATA_DEST_IC 2 --#define CSI_DATA_DEST_IDMAC 4 -- --#define CSI_CCIR_ERR_DET_EN 0x01000000 --#define CSI_HORI_DOWNSIZE_EN 0x80000000 --#define CSI_VERT_DOWNSIZE_EN 0x40000000 --#define CSI_TEST_GEN_MODE_EN 0x01000000 -- --#define CSI_HSC_MASK 0x1fff0000 --#define CSI_HSC_SHIFT 16 --#define CSI_VSC_MASK 0x00000fff --#define CSI_VSC_SHIFT 0 -- --#define CSI_TEST_GEN_R_MASK 0x000000ff --#define CSI_TEST_GEN_R_SHIFT 0 --#define CSI_TEST_GEN_G_MASK 0x0000ff00 --#define CSI_TEST_GEN_G_SHIFT 8 --#define CSI_TEST_GEN_B_MASK 0x00ff0000 --#define CSI_TEST_GEN_B_SHIFT 16 -- --#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 --#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 --#define CSI_SKIP_SMFC_MASK 0x000000f8 --#define CSI_SKIP_SMFC_SHIFT 3 --#define CSI_ID_2_SKIP_MASK 0x00000300 --#define CSI_ID_2_SKIP_SHIFT 8 -- --#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 --#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -- --/* MIPI CSI-2 data types */ --#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ --#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ --#define MIPI_DT_YUV422 0x1e /* UYVY... */ --#define MIPI_DT_RGB444 0x20 --#define MIPI_DT_RGB555 0x21 --#define MIPI_DT_RGB565 0x22 --#define MIPI_DT_RGB666 0x23 --#define MIPI_DT_RGB888 0x24 --#define MIPI_DT_RAW6 0x28 --#define MIPI_DT_RAW7 0x29 --#define MIPI_DT_RAW8 0x2a --#define MIPI_DT_RAW10 0x2b --#define MIPI_DT_RAW12 0x2c --#define MIPI_DT_RAW14 0x2d -- --/* -- * Bitfield of CSI bus signal polarities and modes. -- */ --struct ipu_csi_bus_config { -- unsigned data_width:4; -- unsigned clk_mode:3; -- unsigned ext_vsync:1; -- unsigned vsync_pol:1; -- unsigned hsync_pol:1; -- unsigned pixclk_pol:1; -- unsigned data_pol:1; -- unsigned sens_clksrc:1; -- unsigned pack_tight:1; -- unsigned force_eof:1; -- unsigned data_en_pol:1; -- -- unsigned data_fmt; -- unsigned mipi_dt; --}; -- --/* -- * Enumeration of CSI data bus widths. -- */ --enum ipu_csi_data_width { -- IPU_CSI_DATA_WIDTH_4 = 0, -- IPU_CSI_DATA_WIDTH_8 = 1, -- IPU_CSI_DATA_WIDTH_10 = 3, -- IPU_CSI_DATA_WIDTH_12 = 5, -- IPU_CSI_DATA_WIDTH_16 = 9, --}; -- --/* -- * Enumeration of CSI clock modes. -- */ --enum ipu_csi_clk_mode { -- IPU_CSI_CLK_MODE_GATED_CLK, -- IPU_CSI_CLK_MODE_NONGATED_CLK, -- IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -- IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -- IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, --}; -- --static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) --{ -- return readl(csi->base + offset); --} -- --static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -- unsigned offset) --{ -- writel(value, csi->base + offset); --} -- --/* -- * Set mclk division ratio for generating test mode mclk. Only used -- * for test generator. -- */ --static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -- u32 ipu_clk) --{ -- u32 temp; -- int div_ratio; -- -- div_ratio = (ipu_clk / pixel_clk) - 1; -- -- if (div_ratio > 0xFF || div_ratio < 0) { -- dev_err(csi->ipu->dev, -- "value of pixel_clk extends normal range\n"); -- return -EINVAL; -- } -- -- temp = ipu_csi_read(csi, CSI_SENS_CONF); -- temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -- ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -- CSI_SENS_CONF); -- -- return 0; --} -- --/* -- * Find the CSI data format and data width for the given V4L2 media -- * bus pixel format code. -- */ --static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -- enum v4l2_mbus_type mbus_type) --{ -- switch (mbus_code) { -- case MEDIA_BUS_FMT_BGR565_2X8_BE: -- case MEDIA_BUS_FMT_BGR565_2X8_LE: -- case MEDIA_BUS_FMT_RGB565_2X8_BE: -- case MEDIA_BUS_FMT_RGB565_2X8_LE: -- if (mbus_type == V4L2_MBUS_CSI2_DPHY) -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -- else -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RGB565; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -- cfg->mipi_dt = MIPI_DT_RGB444; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -- cfg->mipi_dt = MIPI_DT_RGB555; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_RGB888_1X24: -- case MEDIA_BUS_FMT_BGR888_1X24: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -- cfg->mipi_dt = MIPI_DT_RGB888; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_YUYV8_2X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_YUV422; -- cfg->data_width = IPU_CSI_DATA_WIDTH_16; -- break; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- case MEDIA_BUS_FMT_Y8_1X8: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -- case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- case MEDIA_BUS_FMT_Y10_1X10: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW10; -- cfg->data_width = IPU_CSI_DATA_WIDTH_10; -- break; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- case MEDIA_BUS_FMT_Y12_1X12: -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -- cfg->mipi_dt = MIPI_DT_RAW12; -- cfg->data_width = IPU_CSI_DATA_WIDTH_12; -- break; -- case MEDIA_BUS_FMT_JPEG_1X8: -- /* TODO */ -- cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -- cfg->mipi_dt = MIPI_DT_RAW8; -- cfg->data_width = IPU_CSI_DATA_WIDTH_8; -- break; -- default: -- return -EINVAL; -- } -- -- return 0; --} -- --/* translate alternate field mode based on given standard */ --static inline enum v4l2_field --ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) --{ -- return (field != V4L2_FIELD_ALTERNATE) ? field : -- ((std & V4L2_STD_525_60) ? -- V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); --} -- --/* -- * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -- */ --static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *mbus_fmt) --{ -- int ret; -- -- memset(csicfg, 0, sizeof(*csicfg)); -- -- ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -- if (ret < 0) -- return ret; -- -- switch (mbus_cfg->type) { -- case V4L2_MBUS_PARALLEL: -- csicfg->ext_vsync = 1; -- csicfg->vsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->hsync_pol = (mbus_cfg->flags & -- V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -- csicfg->pixclk_pol = (mbus_cfg->flags & -- V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -- csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -- break; -- case V4L2_MBUS_BT656: -- csicfg->ext_vsync = 0; -- if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -- mbus_fmt->field == V4L2_FIELD_ALTERNATE) -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -- else -- csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -- break; -- case V4L2_MBUS_CSI2_DPHY: -- /* -- * MIPI CSI-2 requires non gated clock mode, all other -- * parameters are not applicable for MIPI CSI-2 bus. -- */ -- csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -- break; -- default: -- /* will never get here, keep compiler quiet */ -- break; -- } -- -- return 0; --} -- --static int --ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt, -- v4l2_std_id std) --{ -- enum v4l2_field infield, outfield; -- bool swap_fields; -- -- /* get translated field type of input and output */ -- infield = ipu_csi_translate_field(infmt->field, std); -- outfield = ipu_csi_translate_field(outfmt->field, std); -- -- /* -- * Write the H-V-F codes the CSI will match against the -- * incoming data for start/end of active and blanking -- * field intervals. If input and output field types are -- * sequential but not the same (one is SEQ_BT and the other -- * is SEQ_TB), swap the F-bit so that the CSI will capture -- * field 1 lines before field 0 lines. -- */ -- swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -- V4L2_FIELD_IS_SEQUENTIAL(outfield) && -- infield != outfield); -- -- if (!swap_fields) { -- /* -- * Field0BlankEnd = 110, Field0BlankStart = 010 -- * Field0ActiveEnd = 100, Field0ActiveStart = 000 -- * Field1BlankEnd = 111, Field1BlankStart = 011 -- * Field1ActiveEnd = 101, Field1ActiveStart = 001 -- */ -- ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -- } else { -- dev_dbg(csi->ipu->dev, "capture field swap\n"); -- -- /* same as above but with F-bit inverted */ -- ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -- } -- -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- -- return 0; --} -- -- --int ipu_csi_init_interface(struct ipu_csi *csi, -- const struct v4l2_mbus_config *mbus_cfg, -- const struct v4l2_mbus_framefmt *infmt, -- const struct v4l2_mbus_framefmt *outfmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 width, height, data = 0; -- v4l2_std_id std; -- int ret; -- -- ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -- if (ret < 0) -- return ret; -- -- /* set default sensor frame width and height */ -- width = infmt->width; -- height = infmt->height; -- if (infmt->field == V4L2_FIELD_ALTERNATE) -- height *= 2; -- -- /* Set the CSI_SENS_CONF register remaining fields */ -- data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -- cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -- cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -- cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -- cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -- cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -- cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -- cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -- cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -- cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -- cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, data, CSI_SENS_CONF); -- -- /* Set CCIR registers */ -- -- switch (cfg.clk_mode) { -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- if (width == 720 && height == 480) { -- std = V4L2_STD_NTSC; -- height = 525; -- } else if (width == 720 && height == 576) { -- std = V4L2_STD_PAL; -- height = 625; -- } else { -- dev_err(csi->ipu->dev, -- "Unsupported interlaced video mode\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -- if (ret) -- goto out_unlock; -- break; -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -- CSI_CCIR_CODE_1); -- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -- break; -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -- break; -- } -- -- /* Setup sensor frame size */ -- ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -- CSI_SENS_FRM_SIZE); -- -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- --out_unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -- --bool ipu_csi_is_interlaced(struct ipu_csi *csi) --{ -- unsigned long flags; -- u32 sensor_protocol; -- -- spin_lock_irqsave(&csi->lock, flags); -- sensor_protocol = -- (ipu_csi_read(csi, CSI_SENS_CONF) & -- CSI_SENS_CONF_SENS_PRTCL_MASK) >> -- CSI_SENS_CONF_SENS_PRTCL_SHIFT; -- spin_unlock_irqrestore(&csi->lock, flags); -- -- switch (sensor_protocol) { -- case IPU_CSI_CLK_MODE_GATED_CLK: -- case IPU_CSI_CLK_MODE_NONGATED_CLK: -- case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -- return false; -- case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -- case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -- return true; -- default: -- dev_err(csi->ipu->dev, -- "CSI %d sensor protocol unsupported\n", csi->id); -- return false; -- } --} --EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -- --void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -- w->width = (reg & 0xFFFF) + 1; -- w->height = (reg >> 16 & 0xFFFF) + 1; -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -- w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_get_window); -- --void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -- CSI_ACT_FRM_SIZE); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -- reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_window); -- --void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -- reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -- reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -- (vert ? CSI_VERT_DOWNSIZE_EN : 0); -- ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -- --void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -- u32 r_value, u32 g_value, u32 b_value, -- u32 pix_clk) --{ -- unsigned long flags; -- u32 ipu_clk = clk_get_rate(csi->clk_ipu); -- u32 temp; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_TST_CTRL); -- -- if (!active) { -- temp &= ~CSI_TEST_GEN_MODE_EN; -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } else { -- /* Set sensb_mclk div_ratio */ -- ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -- -- temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -- CSI_TEST_GEN_B_MASK); -- temp |= CSI_TEST_GEN_MODE_EN; -- temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -- (g_value << CSI_TEST_GEN_G_SHIFT) | -- (b_value << CSI_TEST_GEN_B_SHIFT); -- ipu_csi_write(csi, temp, CSI_TST_CTRL); -- } -- -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -- --int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -- struct v4l2_mbus_framefmt *mbus_fmt) --{ -- struct ipu_csi_bus_config cfg; -- unsigned long flags; -- u32 temp; -- int ret; -- -- if (vc > 3) -- return -EINVAL; -- -- ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -- if (ret < 0) -- return ret; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_MIPI_DI); -- temp &= ~(0xff << (vc * 8)); -- temp |= (cfg.mipi_dt << (vc * 8)); -- ipu_csi_write(csi, temp, CSI_MIPI_DI); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -- --int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -- u32 max_ratio, u32 id) --{ -- unsigned long flags; -- u32 temp; -- -- if (max_ratio > 5 || id > 3) -- return -EINVAL; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- temp = ipu_csi_read(csi, CSI_SKIP); -- temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -- CSI_SKIP_SMFC_MASK); -- temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -- (id << CSI_ID_2_SKIP_SHIFT) | -- (skip << CSI_SKIP_SMFC_SHIFT); -- ipu_csi_write(csi, temp, CSI_SKIP); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -- --int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) --{ -- unsigned long flags; -- u32 csi_sens_conf, dest; -- -- if (csi_dest == IPU_CSI_DEST_IDMAC) -- dest = CSI_DATA_DEST_IDMAC; -- else -- dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -- -- spin_lock_irqsave(&csi->lock, flags); -- -- csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -- csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -- csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -- ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -- -- spin_unlock_irqrestore(&csi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -- --int ipu_csi_enable(struct ipu_csi *csi) --{ -- ipu_module_enable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_enable); -- --int ipu_csi_disable(struct ipu_csi *csi) --{ -- ipu_module_disable(csi->ipu, csi->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_csi_disable); -- --struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) --{ -- unsigned long flags; -- struct ipu_csi *csi, *ret; -- -- if (id > 1) -- return ERR_PTR(-EINVAL); -- -- csi = ipu->csi_priv[id]; -- ret = csi; -- -- spin_lock_irqsave(&csi->lock, flags); -- -- if (csi->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- csi->inuse = true; --unlock: -- spin_unlock_irqrestore(&csi->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_csi_get); -- --void ipu_csi_put(struct ipu_csi *csi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&csi->lock, flags); -- csi->inuse = false; -- spin_unlock_irqrestore(&csi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_csi_put); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu) --{ -- struct ipu_csi *csi; -- -- if (id > 1) -- return -ENODEV; -- -- csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -- if (!csi) -- return -ENOMEM; -- -- ipu->csi_priv[id] = csi; -- -- spin_lock_init(&csi->lock); -- csi->module = module; -- csi->id = id; -- csi->clk_ipu = clk_ipu; -- csi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!csi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -- id, base, csi->base); -- csi->ipu = ipu; -- -- return 0; --} -- --void ipu_csi_exit(struct ipu_soc *ipu, int id) --{ --} -- --void ipu_csi_dump(struct ipu_csi *csi) --{ -- dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -- ipu_csi_read(csi, CSI_SENS_CONF)); -- dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -- ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -- dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -- ipu_csi_read(csi, CSI_TST_CTRL)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_1)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_2)); -- dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -- ipu_csi_read(csi, CSI_CCIR_CODE_3)); -- dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -- ipu_csi_read(csi, CSI_MIPI_DI)); -- dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -- ipu_csi_read(csi, CSI_SKIP)); --} --EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- a/drivers/gpu/imx/ipu-v3/ipu-dc.c -+++ /dev/null -@@ -1,420 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ -- --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/delay.h> --#include <linux/interrupt.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) --#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -- --#define DC_EVT_NF 0 --#define DC_EVT_NL 1 --#define DC_EVT_EOF 2 --#define DC_EVT_NFIELD 3 --#define DC_EVT_EOL 4 --#define DC_EVT_EOFIELD 5 --#define DC_EVT_NEW_ADDR 6 --#define DC_EVT_NEW_CHAN 7 --#define DC_EVT_NEW_DATA 8 -- --#define DC_EVT_NEW_ADDR_W_0 0 --#define DC_EVT_NEW_ADDR_W_1 1 --#define DC_EVT_NEW_CHAN_W_0 2 --#define DC_EVT_NEW_CHAN_W_1 3 --#define DC_EVT_NEW_DATA_W_0 4 --#define DC_EVT_NEW_DATA_W_1 5 --#define DC_EVT_NEW_ADDR_R_0 6 --#define DC_EVT_NEW_ADDR_R_1 7 --#define DC_EVT_NEW_CHAN_R_0 8 --#define DC_EVT_NEW_CHAN_R_1 9 --#define DC_EVT_NEW_DATA_R_0 10 --#define DC_EVT_NEW_DATA_R_1 11 -- --#define DC_WR_CH_CONF 0x0 --#define DC_WR_CH_ADDR 0x4 --#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -- --#define DC_GEN 0xd4 --#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) --#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) --#define DC_STAT 0x1c8 -- --#define WROD(lf) (0x18 | ((lf) << 1)) --#define WRG 0x01 --#define WCLK 0xc9 -- --#define SYNC_WAVE 0 --#define NULL_WAVE (-1) -- --#define DC_GEN_SYNC_1_6_SYNC (2 << 1) --#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -- --#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) --#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) --#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) --#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) --#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) --#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) --#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) --#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) --#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) --#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -- --#define IPU_DC_NUM_CHANNELS 10 -- --struct ipu_dc_priv; -- --enum ipu_dc_map { -- IPU_DC_MAP_RGB24, -- IPU_DC_MAP_RGB565, -- IPU_DC_MAP_GBR24, /* TVEv2 */ -- IPU_DC_MAP_BGR666, -- IPU_DC_MAP_LVDS666, -- IPU_DC_MAP_BGR24, --}; -- --struct ipu_dc { -- /* The display interface number assigned to this dc channel */ -- unsigned int di; -- void __iomem *base; -- struct ipu_dc_priv *priv; -- int chno; -- bool in_use; --}; -- --struct ipu_dc_priv { -- void __iomem *dc_reg; -- void __iomem *dc_tmpl_reg; -- struct ipu_soc *ipu; -- struct device *dev; -- struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -- struct mutex mutex; -- struct completion comp; -- int use_count; --}; -- --static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_RL_CH(event)); -- reg &= ~(0xffff << (16 * (event & 0x1))); -- reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -- writel(reg, dc->base + DC_RL_CH(event)); --} -- --static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -- int map, int wave, int glue, int sync, int stop) --{ -- struct ipu_dc_priv *priv = dc->priv; -- u32 reg1, reg2; -- -- if (opcode == WCLK) { -- reg1 = (operand << 20) & 0xfff00000; -- reg2 = operand >> 12 | opcode << 1 | stop << 9; -- } else if (opcode == WRG) { -- reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -- reg2 = operand >> 17 | opcode << 7 | stop << 9; -- } else { -- reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -- reg2 = operand >> 12 | opcode << 4 | stop << 9; -- } -- writel(reg1, priv->dc_tmpl_reg + word * 8); -- writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); --} -- --static int ipu_bus_format_to_map(u32 fmt) --{ -- switch (fmt) { -- default: -- WARN_ON(1); -- /* fall-through */ -- case MEDIA_BUS_FMT_RGB888_1X24: -- return IPU_DC_MAP_RGB24; -- case MEDIA_BUS_FMT_RGB565_1X16: -- return IPU_DC_MAP_RGB565; -- case MEDIA_BUS_FMT_GBR888_1X24: -- return IPU_DC_MAP_GBR24; -- case MEDIA_BUS_FMT_RGB666_1X18: -- return IPU_DC_MAP_BGR666; -- case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -- return IPU_DC_MAP_LVDS666; -- case MEDIA_BUS_FMT_BGR888_1X24: -- return IPU_DC_MAP_BGR24; -- } --} -- --int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -- u32 bus_format, u32 width) --{ -- struct ipu_dc_priv *priv = dc->priv; -- int addr, sync; -- u32 reg = 0; -- int map; -- -- dc->di = ipu_di_get_num(di); -- -- map = ipu_bus_format_to_map(bus_format); -- -- /* -- * In interlaced mode we need more counters to create the asymmetric -- * per-field VSYNC signals. The pixel active signal synchronising DC -- * to DI moves to signal generator #6 (see ipu-di.c). In progressive -- * mode counter #5 is used. -- */ -- sync = interlaced ? 6 : 5; -- -- /* Reserve 5 microcode template words for each DI */ -- if (dc->di) -- addr = 5; -- else -- addr = 0; -- -- if (interlaced) { -- dc_link_event(dc, DC_EVT_NL, addr, 3); -- dc_link_event(dc, DC_EVT_EOL, addr, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } else { -- dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -- dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -- dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -- -- /* Init template microcode */ -- dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -- dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -- dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -- dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -- } -- -- dc_link_event(dc, DC_EVT_NF, 0, 0); -- dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_EOF, 0, 0); -- dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -- dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- if (interlaced) -- reg |= DC_WR_CH_CONF_FIELD_MODE; -- else -- reg &= ~DC_WR_CH_CONF_FIELD_MODE; -- writel(reg, dc->base + DC_WR_CH_CONF); -- -- writel(0x0, dc->base + DC_WR_CH_ADDR); -- writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -- --void ipu_dc_enable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable); -- --void ipu_dc_enable_channel(struct ipu_dc *dc) --{ -- u32 reg; -- -- reg = readl(dc->base + DC_WR_CH_CONF); -- reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -- writel(reg, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -- --void ipu_dc_disable_channel(struct ipu_dc *dc) --{ -- u32 val; -- -- val = readl(dc->base + DC_WR_CH_CONF); -- val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -- writel(val, dc->base + DC_WR_CH_CONF); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -- --void ipu_dc_disable(struct ipu_soc *ipu) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_disable); -- --static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -- int byte_num, int offset, int mask) --{ -- int ptr = map * 3 + byte_num; -- u32 reg; -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- reg &= ~(0xffff << (16 * (ptr & 0x1))); -- reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -- -- reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -- reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -- writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) --{ -- u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -- -- writel(reg & ~(0xffff << (16 * (map & 0x1))), -- priv->dc_reg + DC_MAP_CONF_PTR(map)); --} -- --struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) --{ -- struct ipu_dc_priv *priv = ipu->dc_priv; -- struct ipu_dc *dc; -- -- if (channel >= IPU_DC_NUM_CHANNELS) -- return ERR_PTR(-ENODEV); -- -- dc = &priv->channels[channel]; -- -- mutex_lock(&priv->mutex); -- -- if (dc->in_use) { -- mutex_unlock(&priv->mutex); -- return ERR_PTR(-EBUSY); -- } -- -- dc->in_use = true; -- -- mutex_unlock(&priv->mutex); -- -- return dc; --} --EXPORT_SYMBOL_GPL(ipu_dc_get); -- --void ipu_dc_put(struct ipu_dc *dc) --{ -- struct ipu_dc_priv *priv = dc->priv; -- -- mutex_lock(&priv->mutex); -- dc->in_use = false; -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dc_put); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long template_base) --{ -- struct ipu_dc_priv *priv; -- static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -- 0x78, 0, 0x94, 0xb4}; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- priv->dev = dev; -- priv->ipu = ipu; -- priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -- priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -- if (!priv->dc_reg || !priv->dc_tmpl_reg) -- return -ENOMEM; -- -- for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -- priv->channels[i].chno = i; -- priv->channels[i].priv = priv; -- priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -- } -- -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -- DC_WR_CH_CONF_PROG_DI_ID, -- priv->channels[1].base + DC_WR_CH_CONF); -- writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -- priv->channels[5].base + DC_WR_CH_CONF); -- -- writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -- priv->dc_reg + DC_GEN); -- -- ipu->dc_priv = priv; -- -- dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -- base, template_base); -- -- /* rgb24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -- -- /* rgb565 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -- -- /* gbr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -- -- /* bgr666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -- -- /* lvds666 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -- -- /* bgr24 */ -- ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -- ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -- -- return 0; --} -- --void ipu_dc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-di.c -+++ /dev/null -@@ -1,745 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/module.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --struct ipu_di { -- void __iomem *base; -- int id; -- u32 module; -- struct clk *clk_di; /* display input clock */ -- struct clk *clk_ipu; /* IPU bus clock */ -- struct clk *clk_di_pixel; /* resulting pixel clock */ -- bool inuse; -- struct ipu_soc *ipu; --}; -- --static DEFINE_MUTEX(di_mutex); -- --struct di_sync_config { -- int run_count; -- int run_src; -- int offset_count; -- int offset_src; -- int repeat_count; -- int cnt_clr_src; -- int cnt_polarity_gen_en; -- int cnt_polarity_clr_src; -- int cnt_polarity_trigger_src; -- int cnt_up; -- int cnt_down; --}; -- --enum di_pins { -- DI_PIN11 = 0, -- DI_PIN12 = 1, -- DI_PIN13 = 2, -- DI_PIN14 = 3, -- DI_PIN15 = 4, -- DI_PIN16 = 5, -- DI_PIN17 = 6, -- DI_PIN_CS = 7, -- -- DI_PIN_SER_CLK = 0, -- DI_PIN_SER_RS = 1, --}; -- --enum di_sync_wave { -- DI_SYNC_NONE = 0, -- DI_SYNC_CLK = 1, -- DI_SYNC_INT_HSYNC = 2, -- DI_SYNC_HSYNC = 3, -- DI_SYNC_VSYNC = 4, -- DI_SYNC_DE = 6, -- -- DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -- DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -- DI_SYNC_CNT5 = 6, /* counter >= 6 only */ --}; -- --#define SYNC_WAVE 0 -- --#define DI_GENERAL 0x0000 --#define DI_BS_CLKGEN0 0x0004 --#define DI_BS_CLKGEN1 0x0008 --#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) --#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) --#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) --#define DI_SYNC_AS_GEN 0x0054 --#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) --#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) --#define DI_SER_CONF 0x015c --#define DI_SSC 0x0160 --#define DI_POL 0x0164 --#define DI_AW0 0x0168 --#define DI_AW1 0x016c --#define DI_SCR_CONF 0x0170 --#define DI_STAT 0x0174 -- --#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) --#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) --#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) --#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -- --#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) --#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) --#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) --#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) --#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) --#define DI_SW_GEN1_CNT_UP(x) (x) --#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -- --#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 --#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -- --#define DI_GEN_POLARITY_1 (1 << 0) --#define DI_GEN_POLARITY_2 (1 << 1) --#define DI_GEN_POLARITY_3 (1 << 2) --#define DI_GEN_POLARITY_4 (1 << 3) --#define DI_GEN_POLARITY_5 (1 << 4) --#define DI_GEN_POLARITY_6 (1 << 5) --#define DI_GEN_POLARITY_7 (1 << 6) --#define DI_GEN_POLARITY_8 (1 << 7) --#define DI_GEN_POLARITY_DISP_CLK (1 << 17) --#define DI_GEN_DI_CLK_EXT (1 << 20) --#define DI_GEN_DI_VSYNC_EXT (1 << 21) -- --#define DI_POL_DRDY_DATA_POLARITY (1 << 7) --#define DI_POL_DRDY_POLARITY_15 (1 << 4) -- --#define DI_VSYNC_SEL_OFFSET 13 -- --static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) --{ -- return readl(di->base + offset); --} -- --static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) --{ -- writel(value, di->base + offset); --} -- --static void ipu_di_data_wave_config(struct ipu_di *di, -- int wave_gen, -- int access_size, int component_size) --{ -- u32 reg; -- reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -- (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); --} -- --static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -- int set, int up, int down) --{ -- u32 reg; -- -- reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -- reg &= ~(0x3 << (di_pin * 2)); -- reg |= set << (di_pin * 2); -- ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -- -- ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); --} -- --static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -- int start, int count) --{ -- u32 reg; -- int i; -- -- for (i = 0; i < count; i++) { -- struct di_sync_config *c = &config[i]; -- int wave_gen = start + i + 1; -- -- if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -- (c->repeat_count >= 0x1000) || -- (c->cnt_up >= 0x400) || -- (c->cnt_down >= 0x400)) { -- dev_err(di->ipu->dev, "DI%d counters out of range.\n", -- di->id); -- return; -- } -- -- reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -- DI_SW_GEN0_RUN_SRC(c->run_src) | -- DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -- DI_SW_GEN0_OFFSET_SRC(c->offset_src); -- ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -- -- reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -- DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -- DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -- c->cnt_polarity_trigger_src) | -- DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -- DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -- DI_SW_GEN1_CNT_UP(c->cnt_up); -- -- /* Enable auto reload */ -- if (c->repeat_count == 0) -- reg |= DI_SW_GEN1_AUTO_RELOAD; -- -- ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -- -- reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -- reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -- reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -- ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -- } --} -- --static void ipu_di_sync_config_interlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: internal VSYNC for each frame */ -- .run_count = v_total * 2 - 1, -- .run_src = 3, /* == counter 7 */ -- }, { -- /* PIN2: HSYNC waveform */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- }, { -- /* PIN3: VSYNC waveform */ -- .run_count = v_total - 1, -- .run_src = 4, /* == counter 7 */ -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = 4, /* == counter 7 */ -- .cnt_down = sig->mode.vsync_len * 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 4: Field */ -- .run_count = v_total / 2, -- .run_src = DI_SYNC_HSYNC, -- .offset_count = h_total / 2, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = 2, -- .cnt_clr_src = DI_SYNC_CNT1, -- }, { -- /* 5: Active lines */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = (sig->mode.vsync_len + -- sig->mode.vback_porch) / 2, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive / 2, -- .cnt_clr_src = DI_SYNC_CNT4, -- }, { -- /* 6: Active pixel, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = DI_SYNC_CNT5, -- }, { -- /* 7: Half line HSYNC */ -- .run_count = h_total / 2 - 1, -- .run_src = DI_SYNC_CLK, -- } -- }; -- -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- -- ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); --} -- --static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -- struct ipu_di_signal_cfg *sig, int div) --{ -- u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -- sig->mode.hback_porch + sig->mode.hfront_porch; -- u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -- sig->mode.vback_porch + sig->mode.vfront_porch; -- struct di_sync_config cfg[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* PIN2: HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync, -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN3: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* 4: Line Active */ -- .run_src = DI_SYNC_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = DI_SYNC_VSYNC, -- } , { -- /* 5: Pixel Active, referenced by DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 5, /* Line Active */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- } , { -- /* unused */ -- }, -- }; -- /* can't use #7 and #8 for line active and pixel active counters */ -- struct di_sync_config cfg_vga[] = { -- { -- /* 1: INT_HSYNC */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- } , { -- /* 2: VSYNC */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- } , { -- /* 3: Line Active */ -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = sig->mode.vsync_len + -- sig->mode.vback_porch, -- .offset_src = DI_SYNC_INT_HSYNC, -- .repeat_count = sig->mode.vactive, -- .cnt_clr_src = 3 /* VSYNC */, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* 5: Pixel Active signal to DC */ -- .run_src = DI_SYNC_CLK, -- .offset_count = sig->mode.hsync_len + -- sig->mode.hback_porch, -- .offset_src = DI_SYNC_CLK, -- .repeat_count = sig->mode.hactive, -- .cnt_clr_src = 4, /* Line Active */ -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = h_total - 1, -- .run_src = DI_SYNC_CLK, -- .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_CLK, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_CLK, -- .cnt_down = sig->mode.hsync_len * 2, -- } , { -- /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -- .run_count = v_total - 1, -- .run_src = DI_SYNC_INT_HSYNC, -- .offset_count = 1, /* magic value from Freescale TVE driver */ -- .offset_src = DI_SYNC_INT_HSYNC, -- .cnt_polarity_gen_en = 1, -- .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -- .cnt_down = sig->mode.vsync_len * 2, -- } , { -- /* unused */ -- }, -- }; -- -- ipu_di_write(di, v_total - 1, DI_SCR_CONF); -- if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -- ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -- else -- ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); --} -- --static void ipu_di_config_clock(struct ipu_di *di, -- const struct ipu_di_signal_cfg *sig) --{ -- struct clk *clk; -- unsigned clkgen0; -- uint32_t val; -- -- if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -- /* -- * CLKMODE_EXT means we must use the DI clock: this is -- * needed for things like LVDS which needs to feed the -- * DI and LDB with the same pixel clock. -- */ -- clk = di->clk_di; -- -- if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -- /* -- * CLKMODE_SYNC means that we want the DI to be -- * clocked at the same rate as the parent clock. -- * This is needed (eg) for LDB which needs to be -- * fed with the same pixel clock. We assume that -- * the LDB clock has already been set correctly. -- */ -- clkgen0 = 1 << 4; -- } else { -- /* -- * We can use the divider. We should really have -- * a flag here indicating whether the bridge can -- * cope with a fractional divider or not. For the -- * time being, let's go for simplicitly and -- * reliability. -- */ -- unsigned long in_rate; -- unsigned div; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } else { -- /* -- * For other interfaces, we can arbitarily select between -- * the DI specific clock and the internal IPU clock. See -- * DI_GENERAL bit 20. We select the IPU clock if it can -- * give us a clock rate within 1% of the requested frequency, -- * otherwise we use the DI clock. -- */ -- unsigned long rate, clkrate; -- unsigned div, error; -- -- clkrate = clk_get_rate(di->clk_ipu); -- div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- rate = clkrate / div; -- -- error = rate / (sig->mode.pixelclock / 1000); -- -- dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -- rate, div, (signed)(error - 1000) / 10, error % 10); -- -- /* Allow a 1% error */ -- if (error < 1010 && error >= 990) { -- clk = di->clk_ipu; -- -- clkgen0 = div << 4; -- } else { -- unsigned long in_rate; -- unsigned div; -- -- clk = di->clk_di; -- -- clk_set_rate(clk, sig->mode.pixelclock); -- -- in_rate = clk_get_rate(clk); -- div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -- div = clamp(div, 1U, 255U); -- -- clkgen0 = div << 4; -- } -- } -- -- di->clk_di_pixel = clk; -- -- /* Set the divider */ -- ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -- -- /* -- * Set the high/low periods. Bits 24:16 give us the falling edge, -- * and bits 8:0 give the rising edge. LSB is fraction, and is -- * based on the divider above. We want a 50% duty cycle, so set -- * the falling edge to be half the divider. -- */ -- ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -- -- /* Finally select the input clock */ -- val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -- if (clk == di->clk_di) -- val |= DI_GEN_DI_CLK_EXT; -- ipu_di_write(di, val, DI_GENERAL); -- -- dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -- sig->mode.pixelclock, -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- clk == di->clk_di ? "DI" : "IPU", -- clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); --} -- --/* -- * This function is called to adjust a video mode to IPU restrictions. -- * It is meant to be called from drm crtc mode_fixup() methods. -- */ --int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) --{ -- u32 diff; -- -- if (mode->vfront_porch >= 2) -- return 0; -- -- diff = 2 - mode->vfront_porch; -- -- if (mode->vback_porch >= diff) { -- mode->vfront_porch = 2; -- mode->vback_porch -= diff; -- } else if (mode->vsync_len > diff) { -- mode->vfront_porch = 2; -- mode->vsync_len = mode->vsync_len - diff; -- } else { -- dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -- return -EINVAL; -- } -- -- dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -- --static u32 ipu_di_gen_polarity(int pin) --{ -- switch (pin) { -- case 1: -- return DI_GEN_POLARITY_1; -- case 2: -- return DI_GEN_POLARITY_2; -- case 3: -- return DI_GEN_POLARITY_3; -- case 4: -- return DI_GEN_POLARITY_4; -- case 5: -- return DI_GEN_POLARITY_5; -- case 6: -- return DI_GEN_POLARITY_6; -- case 7: -- return DI_GEN_POLARITY_7; -- case 8: -- return DI_GEN_POLARITY_8; -- } -- return 0; --} -- --int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) --{ -- u32 reg; -- u32 di_gen, vsync_cnt; -- u32 div; -- -- dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -- di->id, sig->mode.hactive, sig->mode.vactive); -- -- dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -- clk_get_rate(di->clk_ipu), -- clk_get_rate(di->clk_di), -- sig->mode.pixelclock); -- -- mutex_lock(&di_mutex); -- -- ipu_di_config_clock(di, sig); -- -- div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -- div = div / 16; /* Now divider is integer portion */ -- -- /* Setup pixel clock timing */ -- /* Down time is half of period */ -- ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -- -- ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -- ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -- -- di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -- di_gen |= DI_GEN_DI_VSYNC_EXT; -- -- if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -- ipu_di_sync_config_interlaced(di, sig); -- -- /* set y_sel = 1 */ -- di_gen |= 0x10000000; -- -- vsync_cnt = 3; -- } else { -- ipu_di_sync_config_noninterlaced(di, sig, div); -- -- vsync_cnt = 3; -- if (di->id == 1) -- /* -- * TODO: change only for TVEv2, parallel display -- * uses pin 2 / 3 -- */ -- if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -- vsync_cnt = 6; -- } -- -- if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -- if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -- di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -- -- if (sig->clk_pol) -- di_gen |= DI_GEN_POLARITY_DISP_CLK; -- -- ipu_di_write(di, di_gen, DI_GENERAL); -- -- ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -- DI_SYNC_AS_GEN); -- -- reg = ipu_di_read(di, DI_POL); -- reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -- -- if (sig->enable_pol) -- reg |= DI_POL_DRDY_POLARITY_15; -- if (sig->data_pol) -- reg |= DI_POL_DRDY_DATA_POLARITY; -- -- ipu_di_write(di, reg, DI_POL); -- -- mutex_unlock(&di_mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -- --int ipu_di_enable(struct ipu_di *di) --{ -- int ret; -- -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ret = clk_prepare_enable(di->clk_di_pixel); -- if (ret) -- return ret; -- -- ipu_module_enable(di->ipu, di->module); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_enable); -- --int ipu_di_disable(struct ipu_di *di) --{ -- WARN_ON(IS_ERR(di->clk_di_pixel)); -- -- ipu_module_disable(di->ipu, di->module); -- -- clk_disable_unprepare(di->clk_di_pixel); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_di_disable); -- --int ipu_di_get_num(struct ipu_di *di) --{ -- return di->id; --} --EXPORT_SYMBOL_GPL(ipu_di_get_num); -- --static DEFINE_MUTEX(ipu_di_lock); -- --struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) --{ -- struct ipu_di *di; -- -- if (disp > 1) -- return ERR_PTR(-EINVAL); -- -- di = ipu->di_priv[disp]; -- -- mutex_lock(&ipu_di_lock); -- -- if (di->inuse) { -- di = ERR_PTR(-EBUSY); -- goto out; -- } -- -- di->inuse = true; --out: -- mutex_unlock(&ipu_di_lock); -- -- return di; --} --EXPORT_SYMBOL_GPL(ipu_di_get); -- --void ipu_di_put(struct ipu_di *di) --{ -- mutex_lock(&ipu_di_lock); -- -- di->inuse = false; -- -- mutex_unlock(&ipu_di_lock); --} --EXPORT_SYMBOL_GPL(ipu_di_put); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, -- u32 module, struct clk *clk_ipu) --{ -- struct ipu_di *di; -- -- if (id > 1) -- return -ENODEV; -- -- di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -- if (!di) -- return -ENOMEM; -- -- ipu->di_priv[id] = di; -- -- di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -- if (IS_ERR(di->clk_di)) -- return PTR_ERR(di->clk_di); -- -- di->module = module; -- di->id = id; -- di->clk_ipu = clk_ipu; -- di->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!di->base) -- return -ENOMEM; -- -- ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -- -- dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -- id, base, di->base); -- di->inuse = false; -- di->ipu = ipu; -- -- return 0; --} -- --void ipu_di_exit(struct ipu_soc *ipu, int id) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-dmfc.c -+++ /dev/null -@@ -1,214 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DMFC_RD_CHAN 0x0000 --#define DMFC_WR_CHAN 0x0004 --#define DMFC_WR_CHAN_DEF 0x0008 --#define DMFC_DP_CHAN 0x000c --#define DMFC_DP_CHAN_DEF 0x0010 --#define DMFC_GENERAL1 0x0014 --#define DMFC_GENERAL2 0x0018 --#define DMFC_IC_CTRL 0x001c --#define DMFC_WR_CHAN_ALT 0x0020 --#define DMFC_WR_CHAN_DEF_ALT 0x0024 --#define DMFC_DP_CHAN_ALT 0x0028 --#define DMFC_DP_CHAN_DEF_ALT 0x002c --#define DMFC_GENERAL1_ALT 0x0030 --#define DMFC_STAT 0x0034 -- --#define DMFC_WR_CHAN_1_28 0 --#define DMFC_WR_CHAN_2_41 8 --#define DMFC_WR_CHAN_1C_42 16 --#define DMFC_WR_CHAN_2C_43 24 -- --#define DMFC_DP_CHAN_5B_23 0 --#define DMFC_DP_CHAN_5F_27 8 --#define DMFC_DP_CHAN_6B_24 16 --#define DMFC_DP_CHAN_6F_29 24 -- --struct dmfc_channel_data { -- int ipu_channel; -- unsigned long channel_reg; -- unsigned long shift; -- unsigned eot_shift; -- unsigned max_fifo_lines; --}; -- --static const struct dmfc_channel_data dmfcdata[] = { -- { -- .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5B_23, -- .eot_shift = 20, -- .max_fifo_lines = 3, -- }, { -- .ipu_channel = 24, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6B_24, -- .eot_shift = 22, -- .max_fifo_lines = 1, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_5F_27, -- .eot_shift = 21, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -- .channel_reg = DMFC_WR_CHAN, -- .shift = DMFC_WR_CHAN_1_28, -- .eot_shift = 16, -- .max_fifo_lines = 2, -- }, { -- .ipu_channel = 29, -- .channel_reg = DMFC_DP_CHAN, -- .shift = DMFC_DP_CHAN_6F_29, -- .eot_shift = 23, -- .max_fifo_lines = 1, -- }, --}; -- --#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -- --struct ipu_dmfc_priv; -- --struct dmfc_channel { -- unsigned slots; -- struct ipu_soc *ipu; -- struct ipu_dmfc_priv *priv; -- const struct dmfc_channel_data *data; --}; -- --struct ipu_dmfc_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -- struct mutex mutex; -- void __iomem *base; -- int use_count; --}; -- --int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -- --void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -- --void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) --{ -- struct ipu_dmfc_priv *priv = dmfc->priv; -- u32 dmfc_gen1; -- -- mutex_lock(&priv->mutex); -- -- dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -- -- if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -- dmfc_gen1 |= 1 << dmfc->data->eot_shift; -- else -- dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -- -- writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -- --struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) --{ -- struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -- int i; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) -- if (dmfcdata[i].ipu_channel == ipu_channel) -- return &priv->channels[i]; -- return ERR_PTR(-ENODEV); --} --EXPORT_SYMBOL_GPL(ipu_dmfc_get); -- --void ipu_dmfc_put(struct dmfc_channel *dmfc) --{ --} --EXPORT_SYMBOL_GPL(ipu_dmfc_put); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk) --{ -- struct ipu_dmfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- priv->dev = dev; -- priv->ipu = ipu; -- mutex_init(&priv->mutex); -- -- ipu->dmfc_priv = priv; -- -- for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -- priv->channels[i].priv = priv; -- priv->channels[i].ipu = ipu; -- priv->channels[i].data = &dmfcdata[i]; -- -- if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -- dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -- priv->channels[i].slots = 2; -- } -- -- writel(0x00000050, priv->base + DMFC_WR_CHAN); -- writel(0x00005654, priv->base + DMFC_DP_CHAN); -- writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -- writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -- writel(0x00000003, priv->base + DMFC_GENERAL1); -- -- return 0; --} -- --void ipu_dmfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-dp.c -+++ /dev/null -@@ -1,357 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/export.h> --#include <linux/kernel.h> --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/io.h> --#include <linux/err.h> -- --#include <video/imx-ipu-v3.h> --#include "ipu-prv.h" -- --#define DP_SYNC 0 --#define DP_ASYNC0 0x60 --#define DP_ASYNC1 0xBC -- --#define DP_COM_CONF 0x0 --#define DP_GRAPH_WIND_CTRL 0x0004 --#define DP_FG_POS 0x0008 --#define DP_CSC_A_0 0x0044 --#define DP_CSC_A_1 0x0048 --#define DP_CSC_A_2 0x004C --#define DP_CSC_A_3 0x0050 --#define DP_CSC_0 0x0054 --#define DP_CSC_1 0x0058 -- --#define DP_COM_CONF_FG_EN (1 << 0) --#define DP_COM_CONF_GWSEL (1 << 1) --#define DP_COM_CONF_GWAM (1 << 2) --#define DP_COM_CONF_GWCKE (1 << 3) --#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) --#define DP_COM_CONF_CSC_DEF_OFFSET 8 --#define DP_COM_CONF_CSC_DEF_FG (3 << 8) --#define DP_COM_CONF_CSC_DEF_BG (2 << 8) --#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -- --#define IPUV3_NUM_FLOWS 3 -- --struct ipu_dp_priv; -- --struct ipu_dp { -- u32 flow; -- bool in_use; -- bool foreground; -- enum ipu_color_space in_cs; --}; -- --struct ipu_flow { -- struct ipu_dp foreground; -- struct ipu_dp background; -- enum ipu_color_space out_cs; -- void __iomem *base; -- struct ipu_dp_priv *priv; --}; -- --struct ipu_dp_priv { -- struct ipu_soc *ipu; -- struct device *dev; -- void __iomem *base; -- struct ipu_flow flow[IPUV3_NUM_FLOWS]; -- struct mutex mutex; -- int use_count; --}; -- --static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -- --static inline struct ipu_flow *to_flow(struct ipu_dp *dp) --{ -- if (dp->foreground) -- return container_of(dp, struct ipu_flow, foreground); -- else -- return container_of(dp, struct ipu_flow, background); --} -- --int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -- u8 alpha, bool bg_chan) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- if (bg_chan) -- reg &= ~DP_COM_CONF_GWSEL; -- else -- reg |= DP_COM_CONF_GWSEL; -- writel(reg, flow->base + DP_COM_CONF); -- -- if (enable) { -- reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -- writel(reg | ((u32) alpha << 24), -- flow->base + DP_GRAPH_WIND_CTRL); -- -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } else { -- reg = readl(flow->base + DP_COM_CONF); -- writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -- --int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -- --static void ipu_dp_csc_init(struct ipu_flow *flow, -- enum ipu_color_space in, -- enum ipu_color_space out, -- u32 place) --{ -- u32 reg; -- -- reg = readl(flow->base + DP_COM_CONF); -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- -- if (in == out) { -- writel(reg, flow->base + DP_COM_CONF); -- return; -- } -- -- if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -- writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -- writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -- writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -- writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -- writel(0x3d6 | (0x0000 << 16) | (2 << 30), -- flow->base + DP_CSC_0); -- writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -- flow->base + DP_CSC_1); -- } else { -- writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -- writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -- writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -- writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -- writel(0x000 | (0x3e42 << 16) | (1 << 30), -- flow->base + DP_CSC_0); -- writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -- flow->base + DP_CSC_1); -- } -- -- reg |= place; -- -- writel(reg, flow->base + DP_COM_CONF); --} -- --int ipu_dp_setup_channel(struct ipu_dp *dp, -- enum ipu_color_space in, -- enum ipu_color_space out) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- -- mutex_lock(&priv->mutex); -- -- dp->in_cs = in; -- -- if (!dp->foreground) -- flow->out_cs = out; -- -- if (flow->foreground.in_cs == flow->background.in_cs) { -- /* -- * foreground and background are of same colorspace, put -- * colorspace converter after combining unit. -- */ -- ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -- DP_COM_CONF_CSC_DEF_BOTH); -- } else { -- if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -- flow->foreground.in_cs == flow->out_cs) -- /* -- * foreground identical to output, apply color -- * conversion on background -- */ -- ipu_dp_csc_init(flow, flow->background.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -- else -- ipu_dp_csc_init(flow, flow->foreground.in_cs, -- flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -- } -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -- --int ipu_dp_enable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -- -- priv->use_count++; -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable); -- --int ipu_dp_enable_channel(struct ipu_dp *dp) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg; -- -- if (!dp->foreground) -- return 0; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- reg |= DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- ipu_srm_dp_update(priv->ipu, true); -- -- mutex_unlock(&priv->mutex); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -- --void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) --{ -- struct ipu_flow *flow = to_flow(dp); -- struct ipu_dp_priv *priv = flow->priv; -- u32 reg, csc; -- -- dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -- -- if (!dp->foreground) -- return; -- -- mutex_lock(&priv->mutex); -- -- reg = readl(flow->base + DP_COM_CONF); -- csc = reg & DP_COM_CONF_CSC_DEF_MASK; -- reg &= ~DP_COM_CONF_CSC_DEF_MASK; -- if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -- reg |= DP_COM_CONF_CSC_DEF_BG; -- -- reg &= ~DP_COM_CONF_FG_EN; -- writel(reg, flow->base + DP_COM_CONF); -- -- writel(0, flow->base + DP_FG_POS); -- ipu_srm_dp_update(priv->ipu, sync); -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -- --void ipu_dp_disable(struct ipu_soc *ipu) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- -- mutex_lock(&priv->mutex); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- mutex_unlock(&priv->mutex); --} --EXPORT_SYMBOL_GPL(ipu_dp_disable); -- --struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) --{ -- struct ipu_dp_priv *priv = ipu->dp_priv; -- struct ipu_dp *dp; -- -- if ((flow >> 1) >= IPUV3_NUM_FLOWS) -- return ERR_PTR(-EINVAL); -- -- if (flow & 1) -- dp = &priv->flow[flow >> 1].foreground; -- else -- dp = &priv->flow[flow >> 1].background; -- -- if (dp->in_use) -- return ERR_PTR(-EBUSY); -- -- dp->in_use = true; -- -- return dp; --} --EXPORT_SYMBOL_GPL(ipu_dp_get); -- --void ipu_dp_put(struct ipu_dp *dp) --{ -- dp->in_use = false; --} --EXPORT_SYMBOL_GPL(ipu_dp_put); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) --{ -- struct ipu_dp_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- priv->dev = dev; -- priv->ipu = ipu; -- -- ipu->dp_priv = priv; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- mutex_init(&priv->mutex); -- -- for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -- priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -- priv->flow[i].foreground.foreground = true; -- priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -- priv->flow[i].priv = priv; -- } -- -- return 0; --} -- --void ipu_dp_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-ic.c -+++ /dev/null -@@ -1,761 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2014 Mentor Graphics Inc. -- * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -- */ -- --#include <linux/types.h> --#include <linux/init.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/bitrev.h> --#include <linux/io.h> --#include <linux/err.h> --#include <linux/sizes.h> --#include "ipu-prv.h" -- --/* IC Register Offsets */ --#define IC_CONF 0x0000 --#define IC_PRP_ENC_RSC 0x0004 --#define IC_PRP_VF_RSC 0x0008 --#define IC_PP_RSC 0x000C --#define IC_CMBP_1 0x0010 --#define IC_CMBP_2 0x0014 --#define IC_IDMAC_1 0x0018 --#define IC_IDMAC_2 0x001C --#define IC_IDMAC_3 0x0020 --#define IC_IDMAC_4 0x0024 -- --/* IC Register Fields */ --#define IC_CONF_PRPENC_EN (1 << 0) --#define IC_CONF_PRPENC_CSC1 (1 << 1) --#define IC_CONF_PRPENC_ROT_EN (1 << 2) --#define IC_CONF_PRPVF_EN (1 << 8) --#define IC_CONF_PRPVF_CSC1 (1 << 9) --#define IC_CONF_PRPVF_CSC2 (1 << 10) --#define IC_CONF_PRPVF_CMB (1 << 11) --#define IC_CONF_PRPVF_ROT_EN (1 << 12) --#define IC_CONF_PP_EN (1 << 16) --#define IC_CONF_PP_CSC1 (1 << 17) --#define IC_CONF_PP_CSC2 (1 << 18) --#define IC_CONF_PP_CMB (1 << 19) --#define IC_CONF_PP_ROT_EN (1 << 20) --#define IC_CONF_IC_GLB_LOC_A (1 << 28) --#define IC_CONF_KEY_COLOR_EN (1 << 29) --#define IC_CONF_RWS_EN (1 << 30) --#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -- --#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) --#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) --#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) --#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) --#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) --#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) --#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) --#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) --#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) --#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 --#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) --#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 --#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) --#define IC_IDMAC_1_PP_ROT_OFFSET 17 --#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) --#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) --#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -- --#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) --#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 --#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) --#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 --#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) --#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -- --#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) --#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 --#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) --#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 --#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) --#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -- --struct ic_task_regoffs { -- u32 rsc; -- u32 tpmem_csc[2]; --}; -- --struct ic_task_bitfields { -- u32 ic_conf_en; -- u32 ic_conf_rot_en; -- u32 ic_conf_cmb_en; -- u32 ic_conf_csc1_en; -- u32 ic_conf_csc2_en; -- u32 ic_cmb_galpha_bit; --}; -- --static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .rsc = IC_PRP_ENC_RSC, -- .tpmem_csc = {0x2008, 0}, -- }, -- [IC_TASK_VIEWFINDER] = { -- .rsc = IC_PRP_VF_RSC, -- .tpmem_csc = {0x4028, 0x4040}, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .rsc = IC_PP_RSC, -- .tpmem_csc = {0x6060, 0x6078}, -- }, --}; -- --static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -- [IC_TASK_ENCODER] = { -- .ic_conf_en = IC_CONF_PRPENC_EN, -- .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -- .ic_conf_cmb_en = 0, /* NA */ -- .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -- .ic_conf_csc2_en = 0, /* NA */ -- .ic_cmb_galpha_bit = 0, /* NA */ -- }, -- [IC_TASK_VIEWFINDER] = { -- .ic_conf_en = IC_CONF_PRPVF_EN, -- .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -- .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -- .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -- .ic_cmb_galpha_bit = 0, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .ic_conf_en = IC_CONF_PP_EN, -- .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -- .ic_conf_cmb_en = IC_CONF_PP_CMB, -- .ic_conf_csc1_en = IC_CONF_PP_CSC1, -- .ic_conf_csc2_en = IC_CONF_PP_CSC2, -- .ic_cmb_galpha_bit = 8, -- }, --}; -- --struct ipu_ic_priv; -- --struct ipu_ic { -- enum ipu_ic_task task; -- const struct ic_task_regoffs *reg; -- const struct ic_task_bitfields *bit; -- -- struct ipu_ic_colorspace in_cs; -- struct ipu_ic_colorspace g_in_cs; -- struct ipu_ic_colorspace out_cs; -- -- bool graphics; -- bool rotation; -- bool in_use; -- -- struct ipu_ic_priv *priv; --}; -- --struct ipu_ic_priv { -- void __iomem *base; -- void __iomem *tpmem_base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- int use_count; -- int irt_use_count; -- struct ipu_ic task[IC_NUM_TASKS]; --}; -- --static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) --{ -- return readl(ic->priv->base + offset); --} -- --static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) --{ -- writel(value, ic->priv->base + offset); --} -- --static int init_csc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int csc_index) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 __iomem *base; -- const u16 (*c)[3]; -- const u16 *a; -- u32 param; -- -- base = (u32 __iomem *) -- (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -- -- /* Cast to unsigned */ -- c = (const u16 (*)[3])csc->params.coeff; -- a = (const u16 *)csc->params.offset; -- -- param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -- ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -- writel(param, base++); -- -- param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -- (csc->params.sat << 10); -- writel(param, base++); -- -- param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -- ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -- writel(param, base++); -- -- param = ((a[1] & 0x1fe0) >> 5); -- writel(param, base++); -- -- param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -- ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -- writel(param, base++); -- -- param = ((a[2] & 0x1fe0) >> 5); -- writel(param, base++); -- -- return 0; --} -- --static int calc_resize_coeffs(struct ipu_ic *ic, -- u32 in_size, u32 out_size, -- u32 *resize_coeff, -- u32 *downsize_coeff) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 temp_size, temp_downsize; -- -- /* -- * Input size cannot be more than 4096, and output size cannot -- * be more than 1024 -- */ -- if (in_size > 4096) { -- dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -- return -EINVAL; -- } -- if (out_size > 1024) { -- dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -- return -EINVAL; -- } -- -- /* Cannot downsize more than 4:1 */ -- if ((out_size << 2) < in_size) { -- dev_err(ipu->dev, "Unsupported downsize\n"); -- return -EINVAL; -- } -- -- /* Compute downsizing coefficient */ -- temp_downsize = 0; -- temp_size = in_size; -- while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -- (temp_downsize < 2)) { -- temp_size >>= 1; -- temp_downsize++; -- } -- *downsize_coeff = temp_downsize; -- -- /* -- * compute resizing coefficient using the following equation: -- * resize_coeff = M * (SI - 1) / (SO - 1) -- * where M = 2^13, SI = input size, SO = output size -- */ -- *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -- if (*resize_coeff >= 16384L) { -- dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -- *resize_coeff = 0x3FFF; -- } -- -- return 0; --} -- --void ipu_ic_task_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf |= ic->bit->ic_conf_en; -- -- if (ic->rotation) -- ic_conf |= ic->bit->ic_conf_rot_en; -- -- if (ic->in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->graphics) { -- ic_conf |= ic->bit->ic_conf_cmb_en; -- ic_conf |= ic->bit->ic_conf_csc1_en; -- -- if (ic->g_in_cs.cs != ic->out_cs.cs) -- ic_conf |= ic->bit->ic_conf_csc2_en; -- } -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -- --void ipu_ic_task_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- u32 ic_conf; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- ic_conf &= ~(ic->bit->ic_conf_en | -- ic->bit->ic_conf_csc1_en | -- ic->bit->ic_conf_rot_en); -- if (ic->bit->ic_conf_csc2_en) -- ic_conf &= ~ic->bit->ic_conf_csc2_en; -- if (ic->bit->ic_conf_cmb_en) -- ic_conf &= ~ic->bit->ic_conf_cmb_en; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -- --int ipu_ic_task_graphics_init(struct ipu_ic *ic, -- const struct ipu_ic_colorspace *g_in_cs, -- bool galpha_en, u32 galpha, -- bool colorkey_en, u32 colorkey) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_ic_csc csc2; -- unsigned long flags; -- u32 reg, ic_conf; -- int ret = 0; -- -- if (ic->task == IC_TASK_ENCODER) -- return -EINVAL; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_conf = ipu_ic_read(ic, IC_CONF); -- -- if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -- struct ipu_ic_csc csc1; -- -- ret = ipu_ic_calc_csc(&csc1, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB, -- V4L2_YCBCR_ENC_601, -- V4L2_QUANTIZATION_FULL_RANGE, -- IPUV3_COLORSPACE_RGB); -- if (ret) -- goto unlock; -- -- /* need transparent CSC1 conversion */ -- ret = init_csc(ic, &csc1, 0); -- if (ret) -- goto unlock; -- } -- -- ic->g_in_cs = *g_in_cs; -- csc2.in_cs = ic->g_in_cs; -- csc2.out_cs = ic->out_cs; -- -- ret = __ipu_ic_calc_csc(&csc2); -- if (ret) -- goto unlock; -- -- ret = init_csc(ic, &csc2, 1); -- if (ret) -- goto unlock; -- -- if (galpha_en) { -- ic_conf |= IC_CONF_IC_GLB_LOC_A; -- reg = ipu_ic_read(ic, IC_CMBP_1); -- reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -- reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -- ipu_ic_write(ic, reg, IC_CMBP_1); -- } else -- ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -- -- if (colorkey_en) { -- ic_conf |= IC_CONF_KEY_COLOR_EN; -- ipu_ic_write(ic, colorkey, IC_CMBP_2); -- } else -- ic_conf &= ~IC_CONF_KEY_COLOR_EN; -- -- ipu_ic_write(ic, ic_conf, IC_CONF); -- -- ic->graphics = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -- --int ipu_ic_task_init_rsc(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height, -- u32 rsc) --{ -- struct ipu_ic_priv *priv = ic->priv; -- u32 downsize_coeff, resize_coeff; -- unsigned long flags; -- int ret = 0; -- -- if (!rsc) { -- /* Setup vertical resizing */ -- -- ret = calc_resize_coeffs(ic, in_height, out_height, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc = (downsize_coeff << 30) | (resize_coeff << 16); -- -- /* Setup horizontal resizing */ -- ret = calc_resize_coeffs(ic, in_width, out_width, -- &resize_coeff, &downsize_coeff); -- if (ret) -- return ret; -- -- rsc |= (downsize_coeff << 14) | resize_coeff; -- } -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ipu_ic_write(ic, rsc, ic->reg->rsc); -- -- /* Setup color space conversion */ -- ic->in_cs = csc->in_cs; -- ic->out_cs = csc->out_cs; -- -- ret = init_csc(ic, csc, 0); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} -- --int ipu_ic_task_init(struct ipu_ic *ic, -- const struct ipu_ic_csc *csc, -- int in_width, int in_height, -- int out_width, int out_height) --{ -- return ipu_ic_task_init_rsc(ic, csc, -- in_width, in_height, -- out_width, out_height, 0); --} --EXPORT_SYMBOL_GPL(ipu_ic_task_init); -- --int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -- u32 width, u32 height, int burst_size, -- enum ipu_rotate_mode rot) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -- u32 temp_rot = bitrev8(rot) >> 5; -- bool need_hor_flip = false; -- unsigned long flags; -- int ret = 0; -- -- if ((burst_size != 8) && (burst_size != 16)) { -- dev_err(ipu->dev, "Illegal burst length for IC\n"); -- return -EINVAL; -- } -- -- width--; -- height--; -- -- if (temp_rot & 0x2) /* Need horizontal flip */ -- need_hor_flip = true; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -- ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -- ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -- -- switch (channel->num) { -- case IPUV3_CHANNEL_IC_PP_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -- break; -- case IPUV3_CHANNEL_MEM_ROT_PP: -- ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -- break; -- case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_ENC: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_IC_PRP_VF_MEM: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -- -- if (need_hor_flip) -- ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -- -- ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -- ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -- -- ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -- ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -- break; -- case IPUV3_CHANNEL_MEM_ROT_VF: -- ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -- ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -- break; -- case IPUV3_CHANNEL_G_MEM_IC_PP: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -- break; -- case IPUV3_CHANNEL_VDI_MEM_IC_VF: -- if (burst_size == 16) -- ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -- else -- ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -- break; -- default: -- goto unlock; -- } -- -- ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -- ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -- ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -- -- if (ipu_rot_mode_is_irt(rot)) -- ic->rotation = true; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -- --static void ipu_irt_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (!priv->irt_use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -- -- priv->irt_use_count++; --} -- --static void ipu_irt_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- -- if (priv->irt_use_count) { -- if (!--priv->irt_use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -- } --} -- --int ipu_ic_enable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -- -- priv->use_count++; -- -- if (ic->rotation) -- ipu_irt_enable(ic); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_enable); -- --int ipu_ic_disable(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- if (ic->rotation) -- ipu_irt_disable(ic); -- -- ic->rotation = ic->graphics = false; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_ic_disable); -- --struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) --{ -- struct ipu_ic_priv *priv = ipu->ic_priv; -- unsigned long flags; -- struct ipu_ic *ic, *ret; -- -- if (task >= IC_NUM_TASKS) -- return ERR_PTR(-EINVAL); -- -- ic = &priv->task[task]; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (ic->in_use) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- ic->in_use = true; -- ret = ic; -- --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_ic_get); -- --void ipu_ic_put(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- ic->in_use = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_ic_put); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base) --{ -- struct ipu_ic_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->ic_priv = priv; -- -- spin_lock_init(&priv->lock); -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -- if (!priv->tpmem_base) -- return -ENOMEM; -- -- dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -- -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- priv->task[i].task = i; -- priv->task[i].priv = priv; -- priv->task[i].reg = &ic_task_reg[i]; -- priv->task[i].bit = &ic_task_bit[i]; -- } -- -- return 0; --} -- --void ipu_ic_exit(struct ipu_soc *ipu) --{ --} -- --void ipu_ic_dump(struct ipu_ic *ic) --{ -- struct ipu_ic_priv *priv = ic->priv; -- struct ipu_soc *ipu = priv->ipu; -- -- dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -- ipu_ic_read(ic, IC_CONF)); -- dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_ENC_RSC)); -- dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PRP_VF_RSC)); -- dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -- ipu_ic_read(ic, IC_PP_RSC)); -- dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_1)); -- dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_CMBP_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_1)); -- dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_2)); -- dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_3)); -- dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -- ipu_ic_read(ic, IC_IDMAC_4)); --} --EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- a/drivers/gpu/imx/ipu-v3/ipu-image-convert.c -+++ /dev/null -@@ -1,2475 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * -- * Queued image conversion support, with tiling and rotation. -- */ -- --#include <linux/interrupt.h> --#include <linux/dma-mapping.h> --#include <video/imx-ipu-image-convert.h> --#include "ipu-prv.h" -- --/* -- * The IC Resizer has a restriction that the output frame from the -- * resizer must be 1024 or less in both width (pixels) and height -- * (lines). -- * -- * The image converter attempts to split up a conversion when -- * the desired output (converted) frame resolution exceeds the -- * IC resizer limit of 1024 in either dimension. -- * -- * If either dimension of the output frame exceeds the limit, the -- * dimension is split into 1, 2, or 4 equal stripes, for a maximum -- * of 4*4 or 16 tiles. A conversion is then carried out for each -- * tile (but taking care to pass the full frame stride length to -- * the DMA channel's parameter memory!). IDMA double-buffering is used -- * to convert each tile back-to-back when possible (see note below -- * when double_buffering boolean is set). -- * -- * Note that the input frame must be split up into the same number -- * of tiles as the output frame: -- * -- * +---------+-----+ -- * +-----+---+ | A | B | -- * | A | B | | | | -- * +-----+---+ --> +---------+-----+ -- * | C | D | | C | D | -- * +-----+---+ | | | -- * +---------+-----+ -- * -- * Clockwise 90° rotations are handled by first rescaling into a -- * reusable temporary tile buffer and then rotating with the 8x8 -- * block rotator, writing to the correct destination: -- * -- * +-----+-----+ -- * | | | -- * +-----+---+ +---------+ | C | A | -- * | A | B | | A,B, | | | | | -- * +-----+---+ --> | C,D | | --> | | | -- * | C | D | +---------+ +-----+-----+ -- * +-----+---+ | D | B | -- * | | | -- * +-----+-----+ -- * -- * If the 8x8 block rotator is used, horizontal or vertical flipping -- * is done during the rotation step, otherwise flipping is done -- * during the scaling step. -- * With rotation or flipping, tile order changes between input and -- * output image. Tiles are numbered row major from top left to bottom -- * right for both input and output image. -- */ -- --#define MAX_STRIPES_W 4 --#define MAX_STRIPES_H 4 --#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -- --#define MIN_W 16 --#define MIN_H 8 --#define MAX_W 4096 --#define MAX_H 4096 -- --enum ipu_image_convert_type { -- IMAGE_CONVERT_IN = 0, -- IMAGE_CONVERT_OUT, --}; -- --struct ipu_image_convert_dma_buf { -- void *virt; -- dma_addr_t phys; -- unsigned long len; --}; -- --struct ipu_image_convert_dma_chan { -- int in; -- int out; -- int rot_in; -- int rot_out; -- int vdi_in_p; -- int vdi_in; -- int vdi_in_n; --}; -- --/* dimensions of one tile */ --struct ipu_image_tile { -- u32 width; -- u32 height; -- u32 left; -- u32 top; -- /* size and strides are in bytes */ -- u32 size; -- u32 stride; -- u32 rot_stride; -- /* start Y or packed offset of this tile */ -- u32 offset; -- /* offset from start to tile in U plane, for planar formats */ -- u32 u_off; -- /* offset from start to tile in V plane, for planar formats */ -- u32 v_off; --}; -- --struct ipu_image_convert_image { -- struct ipu_image base; -- enum ipu_image_convert_type type; -- -- const struct ipu_image_pixfmt *fmt; -- unsigned int stride; -- -- /* # of rows (horizontal stripes) if dest height is > 1024 */ -- unsigned int num_rows; -- /* # of columns (vertical stripes) if dest width is > 1024 */ -- unsigned int num_cols; -- -- struct ipu_image_tile tile[MAX_TILES]; --}; -- --struct ipu_image_pixfmt { -- u32 fourcc; /* V4L2 fourcc */ -- int bpp; /* total bpp */ -- int uv_width_dec; /* decimation in width for U/V planes */ -- int uv_height_dec; /* decimation in height for U/V planes */ -- bool planar; /* planar format */ -- bool uv_swapped; /* U and V planes are swapped */ -- bool uv_packed; /* partial planar (U and V in same plane) */ --}; -- --struct ipu_image_convert_ctx; --struct ipu_image_convert_chan; --struct ipu_image_convert_priv; -- --struct ipu_image_convert_ctx { -- struct ipu_image_convert_chan *chan; -- -- ipu_image_convert_cb_t complete; -- void *complete_context; -- -- /* Source/destination image data and rotation mode */ -- struct ipu_image_convert_image in; -- struct ipu_image_convert_image out; -- struct ipu_ic_csc csc; -- enum ipu_rotate_mode rot_mode; -- u32 downsize_coeff_h; -- u32 downsize_coeff_v; -- u32 image_resize_coeff_h; -- u32 image_resize_coeff_v; -- u32 resize_coeffs_h[MAX_STRIPES_W]; -- u32 resize_coeffs_v[MAX_STRIPES_H]; -- -- /* intermediate buffer for rotation */ -- struct ipu_image_convert_dma_buf rot_intermediate[2]; -- -- /* current buffer number for double buffering */ -- int cur_buf_num; -- -- bool aborting; -- struct completion aborted; -- -- /* can we use double-buffering for this conversion operation? */ -- bool double_buffering; -- /* num_rows * num_cols */ -- unsigned int num_tiles; -- /* next tile to process */ -- unsigned int next_tile; -- /* where to place converted tile in dest image */ -- unsigned int out_tile_map[MAX_TILES]; -- -- struct list_head list; --}; -- --struct ipu_image_convert_chan { -- struct ipu_image_convert_priv *priv; -- -- enum ipu_ic_task ic_task; -- const struct ipu_image_convert_dma_chan *dma_ch; -- -- struct ipu_ic *ic; -- struct ipuv3_channel *in_chan; -- struct ipuv3_channel *out_chan; -- struct ipuv3_channel *rotation_in_chan; -- struct ipuv3_channel *rotation_out_chan; -- -- /* the IPU end-of-frame irqs */ -- int out_eof_irq; -- int rot_out_eof_irq; -- -- spinlock_t irqlock; -- -- /* list of convert contexts */ -- struct list_head ctx_list; -- /* queue of conversion runs */ -- struct list_head pending_q; -- /* queue of completed runs */ -- struct list_head done_q; -- -- /* the current conversion run */ -- struct ipu_image_convert_run *current_run; --}; -- --struct ipu_image_convert_priv { -- struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -- struct ipu_soc *ipu; --}; -- --static const struct ipu_image_convert_dma_chan --image_convert_dma_chan[IC_NUM_TASKS] = { -- [IC_TASK_VIEWFINDER] = { -- .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -- .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -- .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -- .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -- .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -- .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -- }, -- [IC_TASK_POST_PROCESSOR] = { -- .in = IPUV3_CHANNEL_MEM_IC_PP, -- .out = IPUV3_CHANNEL_IC_PP_MEM, -- .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -- .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -- }, --}; -- --static const struct ipu_image_pixfmt image_convert_formats[] = { -- { -- .fourcc = V4L2_PIX_FMT_RGB565, -- .bpp = 16, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR24, -- .bpp = 24, -- }, { -- .fourcc = V4L2_PIX_FMT_RGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XRGB32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_XBGR32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_BGRX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_RGBX32, -- .bpp = 32, -- }, { -- .fourcc = V4L2_PIX_FMT_YUYV, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_UYVY, -- .bpp = 16, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- }, { -- .fourcc = V4L2_PIX_FMT_YVU420, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_swapped = true, -- }, { -- .fourcc = V4L2_PIX_FMT_NV12, -- .bpp = 12, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 2, -- .uv_packed = true, -- }, { -- .fourcc = V4L2_PIX_FMT_YUV422P, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- }, { -- .fourcc = V4L2_PIX_FMT_NV16, -- .bpp = 16, -- .planar = true, -- .uv_width_dec = 2, -- .uv_height_dec = 1, -- .uv_packed = true, -- }, --}; -- --static const struct ipu_image_pixfmt *get_format(u32 fourcc) --{ -- const struct ipu_image_pixfmt *ret = NULL; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -- if (image_convert_formats[i].fourcc == fourcc) { -- ret = &image_convert_formats[i]; -- break; -- } -- } -- -- return ret; --} -- --static void dump_format(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -- chan->ic_task, ctx, -- ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -- ic_image->base.pix.width, ic_image->base.pix.height, -- ic_image->num_cols, ic_image->num_rows, -- ic_image->fmt->fourcc & 0xff, -- (ic_image->fmt->fourcc >> 8) & 0xff, -- (ic_image->fmt->fourcc >> 16) & 0xff, -- (ic_image->fmt->fourcc >> 24) & 0xff); --} -- --int ipu_image_convert_enum_format(int index, u32 *fourcc) --{ -- const struct ipu_image_pixfmt *fmt; -- -- if (index >= (int)ARRAY_SIZE(image_convert_formats)) -- return -EINVAL; -- -- /* Format found */ -- fmt = &image_convert_formats[index]; -- *fourcc = fmt->fourcc; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -- --static void free_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf) --{ -- if (buf->virt) -- dma_free_coherent(priv->ipu->dev, -- buf->len, buf->virt, buf->phys); -- buf->virt = NULL; -- buf->phys = 0; --} -- --static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -- struct ipu_image_convert_dma_buf *buf, -- int size) --{ -- buf->len = PAGE_ALIGN(size); -- buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -- GFP_DMA | GFP_KERNEL); -- if (!buf->virt) { -- dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -- return -ENOMEM; -- } -- -- return 0; --} -- --static inline int num_stripes(int dim) --{ -- return (dim - 1) / 1024 + 1; --} -- --/* -- * Calculate downsizing coefficients, which are the same for all tiles, -- * and initial bilinear resizing coefficients, which are used to find the -- * best seam positions. -- * Also determine the number of tiles necessary to guarantee that no tile -- * is larger than 1024 pixels in either dimension at the output and between -- * IC downsizing and main processing sections. -- */ --static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -- struct ipu_image *in, -- struct ipu_image *out) --{ -- u32 downsized_width = in->rect.width; -- u32 downsized_height = in->rect.height; -- u32 downsize_coeff_v = 0; -- u32 downsize_coeff_h = 0; -- u32 resized_width = out->rect.width; -- u32 resized_height = out->rect.height; -- u32 resize_coeff_h; -- u32 resize_coeff_v; -- u32 cols; -- u32 rows; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- resized_width = out->rect.height; -- resized_height = out->rect.width; -- } -- -- /* Do not let invalid input lead to an endless loop below */ -- if (WARN_ON(resized_width == 0 || resized_height == 0)) -- return -EINVAL; -- -- while (downsized_width >= resized_width * 2) { -- downsized_width >>= 1; -- downsize_coeff_h++; -- } -- -- while (downsized_height >= resized_height * 2) { -- downsized_height >>= 1; -- downsize_coeff_v++; -- } -- -- /* -- * Calculate the bilinear resizing coefficients that could be used if -- * we were converting with a single tile. The bottom right output pixel -- * should sample as close as possible to the bottom right input pixel -- * out of the decimator, but not overshoot it: -- */ -- resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -- resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -- -- /* -- * Both the output of the IC downsizing section before being passed to -- * the IC main processing section and the final output of the IC main -- * processing section must be <= 1024 pixels in both dimensions. -- */ -- cols = num_stripes(max_t(u32, downsized_width, resized_width)); -- rows = num_stripes(max_t(u32, downsized_height, resized_height)); -- -- dev_dbg(ctx->chan->priv->ipu->dev, -- "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -- __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -- resize_coeff_v, cols, rows); -- -- if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -- resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -- return -EINVAL; -- -- ctx->downsize_coeff_h = downsize_coeff_h; -- ctx->downsize_coeff_v = downsize_coeff_v; -- ctx->image_resize_coeff_h = resize_coeff_h; -- ctx->image_resize_coeff_v = resize_coeff_v; -- ctx->in.num_cols = cols; -- ctx->in.num_rows = rows; -- -- return 0; --} -- --#define round_closest(x, y) round_down((x) + (y)/2, (y)) -- --/* -- * Find the best aligned seam position for the given column / row index. -- * Rotation and image offsets are out of scope. -- * -- * @index: column / row index, used to calculate valid interval -- * @in_edge: input right / bottom edge -- * @out_edge: output right / bottom edge -- * @in_align: input alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format -- * @out_align: output alignment, either horizontal 8-byte line start address -- * alignment, or pixel alignment due to image format or rotator -- * block size -- * @in_burst: horizontal input burst size in case of horizontal flip -- * @out_burst: horizontal output burst size or rotator block size -- * @downsize_coeff: downsizing section coefficient -- * @resize_coeff: main processing section resizing coefficient -- * @_in_seam: aligned input seam position return value -- * @_out_seam: aligned output seam position return value -- */ --static void find_best_seam(struct ipu_image_convert_ctx *ctx, -- unsigned int index, -- unsigned int in_edge, -- unsigned int out_edge, -- unsigned int in_align, -- unsigned int out_align, -- unsigned int in_burst, -- unsigned int out_burst, -- unsigned int downsize_coeff, -- unsigned int resize_coeff, -- u32 *_in_seam, -- u32 *_out_seam) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int out_pos; -- /* Input / output seam position candidates */ -- unsigned int out_seam = 0; -- unsigned int in_seam = 0; -- unsigned int min_diff = UINT_MAX; -- unsigned int out_start; -- unsigned int out_end; -- unsigned int in_start; -- unsigned int in_end; -- -- /* Start within 1024 pixels of the right / bottom edge */ -- out_start = max_t(int, index * out_align, out_edge - 1024); -- /* End before having to add more columns to the left / rows above */ -- out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -- -- /* -- * Limit input seam position to make sure that the downsized input tile -- * to the right or bottom does not exceed 1024 pixels. -- */ -- in_start = max_t(int, index * in_align, -- in_edge - (1024 << downsize_coeff)); -- in_end = min_t(unsigned int, in_edge, -- index * (1024 << downsize_coeff) + 1); -- -- /* -- * Output tiles must start at a multiple of 8 bytes horizontally and -- * possibly at an even line horizontally depending on the pixel format. -- * Only consider output aligned positions for the seam. -- */ -- out_start = round_up(out_start, out_align); -- for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -- unsigned int in_pos; -- unsigned int in_pos_aligned; -- unsigned int in_pos_rounded; -- unsigned int abs_diff; -- -- /* -- * Tiles in the right row / bottom column may not be allowed to -- * overshoot horizontally / vertically. out_burst may be the -- * actual DMA burst size, or the rotator block size. -- */ -- if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -- continue; -- -- /* -- * Input sample position, corresponding to out_pos, 19.13 fixed -- * point. -- */ -- in_pos = (out_pos * resize_coeff) << downsize_coeff; -- /* -- * The closest input sample position that we could actually -- * start the input tile at, 19.13 fixed point. -- */ -- in_pos_aligned = round_closest(in_pos, 8192U * in_align); -- /* Convert 19.13 fixed point to integer */ -- in_pos_rounded = in_pos_aligned / 8192U; -- -- if (in_pos_rounded < in_start) -- continue; -- if (in_pos_rounded >= in_end) -- break; -- -- if ((in_burst > 1) && -- (in_edge - in_pos_rounded) % in_burst) -- continue; -- -- if (in_pos < in_pos_aligned) -- abs_diff = in_pos_aligned - in_pos; -- else -- abs_diff = in_pos - in_pos_aligned; -- -- if (abs_diff < min_diff) { -- in_seam = in_pos_rounded; -- out_seam = out_pos; -- min_diff = abs_diff; -- } -- } -- -- *_out_seam = out_seam; -- *_in_seam = in_seam; -- -- dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -- __func__, out_seam, out_align, out_start, out_end, -- in_seam, in_align, in_start, in_end, min_diff / 8192, -- DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); --} -- --/* -- * Tile left edges are required to be aligned to multiples of 8 bytes -- * by the IDMAC. -- */ --static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) --{ -- if (fmt->planar) -- return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -- else -- return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; --} -- --/* -- * Tile top edge alignment is only limited by chroma subsampling. -- */ --static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) --{ -- return fmt->uv_height_dec > 1 ? 2 : 1; --} -- --static inline u32 tile_width_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN) { -- /* -- * The IC burst reads 8 pixels at a time. Reading beyond the -- * end of the line is usually acceptable. Those pixels are -- * ignored, unless the IC has to write the scaled line in -- * reverse. -- */ -- return (!ipu_rot_mode_is_irt(rot_mode) && -- (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -- } -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (ipu_rot_mode_is_irt(rot_mode) && -- fmt->planar && !fmt->uv_packed) ? -- 8 * fmt->uv_width_dec : 8; --} -- --static inline u32 tile_height_align(enum ipu_image_convert_type type, -- const struct ipu_image_pixfmt *fmt, -- enum ipu_rotate_mode rot_mode) --{ -- if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -- return 2; -- -- /* -- * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -- * formats to guarantee 8-byte aligned line start addresses in the -- * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -- * for all other formats. -- */ -- return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; --} -- --/* -- * Fill in left position and width and for all tiles in an input column, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a row, and output tile top position and height are set. -- */ --static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -- unsigned int col, -- struct ipu_image_convert_image *in, -- unsigned int in_left, unsigned int in_width, -- struct ipu_image_convert_image *out, -- unsigned int out_left, unsigned int out_width) --{ -- unsigned int row, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (row = 0; row < in->num_rows; row++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->left = in_left; -- in_tile->width = in_width; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->top = out_left; -- out_tile->height = out_width; -- } else { -- out_tile->left = out_left; -- out_tile->width = out_width; -- } -- } --} -- --/* -- * Fill in top position and height and for all tiles in an input row, and -- * for all corresponding output tiles. If the 90° rotator is used, the output -- * tiles are in a column, and output tile left position and width are set. -- */ --static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -- struct ipu_image_convert_image *in, -- unsigned int in_top, unsigned int in_height, -- struct ipu_image_convert_image *out, -- unsigned int out_top, unsigned int out_height) --{ -- unsigned int col, tile_idx; -- struct ipu_image_tile *in_tile, *out_tile; -- -- for (col = 0; col < in->num_cols; col++) { -- tile_idx = in->num_cols * row + col; -- in_tile = &in->tile[tile_idx]; -- out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -- -- in_tile->top = in_top; -- in_tile->height = in_height; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- out_tile->left = out_top; -- out_tile->width = out_height; -- } else { -- out_tile->top = out_top; -- out_tile->height = out_height; -- } -- } --} -- --/* -- * Find the best horizontal and vertical seam positions to split into tiles. -- * Minimize the fractional part of the input sampling position for the -- * top / left pixels of each tile. -- */ --static void find_seams(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *in, -- struct ipu_image_convert_image *out) --{ -- struct device *dev = ctx->chan->priv->ipu->dev; -- unsigned int resized_width = out->base.rect.width; -- unsigned int resized_height = out->base.rect.height; -- unsigned int col; -- unsigned int row; -- unsigned int in_left_align = tile_left_align(in->fmt); -- unsigned int in_top_align = tile_top_align(in->fmt); -- unsigned int out_left_align = tile_left_align(out->fmt); -- unsigned int out_top_align = tile_top_align(out->fmt); -- unsigned int out_width_align = tile_width_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int out_height_align = tile_height_align(out->type, out->fmt, -- ctx->rot_mode); -- unsigned int in_right = in->base.rect.width; -- unsigned int in_bottom = in->base.rect.height; -- unsigned int out_right = out->base.rect.width; -- unsigned int out_bottom = out->base.rect.height; -- unsigned int flipped_out_left; -- unsigned int flipped_out_top; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* Switch width/height and align top left to IRT block size */ -- resized_width = out->base.rect.height; -- resized_height = out->base.rect.width; -- out_left_align = out_height_align; -- out_top_align = out_width_align; -- out_width_align = out_left_align; -- out_height_align = out_top_align; -- out_right = out->base.rect.height; -- out_bottom = out->base.rect.width; -- } -- -- for (col = in->num_cols - 1; col > 0; col--) { -- bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- bool allow_out_overshoot = (col < in->num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- unsigned int in_left; -- unsigned int out_left; -- -- /* -- * Align input width to burst length if the scaling step flips -- * horizontally. -- */ -- -- find_best_seam(ctx, col, -- in_right, out_right, -- in_left_align, out_left_align, -- allow_in_overshoot ? 1 : 8 /* burst length */, -- allow_out_overshoot ? 1 : out_width_align, -- ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -- &in_left, &out_left); -- -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- flipped_out_left = resized_width - out_right; -- else -- flipped_out_left = out_left; -- -- fill_tile_column(ctx, col, in, in_left, in_right - in_left, -- out, flipped_out_left, out_right - out_left); -- -- dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -- in_left, in_right - in_left, -- flipped_out_left, out_right - out_left); -- -- in_right = in_left; -- out_right = out_left; -- } -- -- flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -- resized_width - out_right : 0; -- -- fill_tile_column(ctx, 0, in, 0, in_right, -- out, flipped_out_left, out_right); -- -- dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -- in_right, flipped_out_left, out_right); -- -- for (row = in->num_rows - 1; row > 0; row--) { -- bool allow_overshoot = row < in->num_rows - 1; -- unsigned int in_top; -- unsigned int out_top; -- -- find_best_seam(ctx, row, -- in_bottom, out_bottom, -- in_top_align, out_top_align, -- 1, allow_overshoot ? 1 : out_height_align, -- ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -- &in_top, &out_top); -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = out_top; -- -- fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -- out, flipped_out_top, out_bottom - out_top); -- -- dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -- in_top, in_bottom - in_top, -- flipped_out_top, out_bottom - out_top); -- -- in_bottom = in_top; -- out_bottom = out_top; -- } -- -- if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -- ipu_rot_mode_is_irt(ctx->rot_mode)) -- flipped_out_top = resized_height - out_bottom; -- else -- flipped_out_top = 0; -- -- fill_tile_row(ctx, 0, in, 0, in_bottom, -- out, flipped_out_top, out_bottom); -- -- dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -- in_bottom, flipped_out_top, out_bottom); --} -- --static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned int max_width = 1024; -- unsigned int max_height = 1024; -- unsigned int i; -- -- if (image->type == IMAGE_CONVERT_IN) { -- /* Up to 4096x4096 input tile size */ -- max_width <<= ctx->downsize_coeff_h; -- max_height <<= ctx->downsize_coeff_v; -- } -- -- for (i = 0; i < ctx->num_tiles; i++) { -- struct ipu_image_tile *tile; -- const unsigned int row = i / image->num_cols; -- const unsigned int col = i % image->num_cols; -- -- if (image->type == IMAGE_CONVERT_OUT) -- tile = &image->tile[ctx->out_tile_map[i]]; -- else -- tile = &image->tile[i]; -- -- tile->size = ((tile->height * image->fmt->bpp) >> 3) * -- tile->width; -- -- if (image->fmt->planar) { -- tile->stride = tile->width; -- tile->rot_stride = tile->height; -- } else { -- tile->stride = -- (image->fmt->bpp * tile->width) >> 3; -- tile->rot_stride = -- (image->fmt->bpp * tile->height) >> 3; -- } -- -- dev_dbg(priv->ipu->dev, -- "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -- row, col, -- tile->width, tile->height, tile->left, tile->top); -- -- if (!tile->width || tile->width > max_width || -- !tile->height || tile->height > max_height) { -- dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -- image->type == IMAGE_CONVERT_IN ? "input" : -- "output", tile->width, tile->height); -- return -EINVAL; -- } -- } -- -- return 0; --} -- --/* -- * Use the rotation transformation to find the tile coordinates -- * (row, col) of a tile in the destination frame that corresponds -- * to the given tile coordinates of a source frame. The destination -- * coordinate is then converted to a tile index. -- */ --static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -- int src_row, int src_col) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- int dst_row, dst_col; -- -- /* with no rotation it's a 1:1 mapping */ -- if (ctx->rot_mode == IPU_ROTATE_NONE) -- return src_row * s_image->num_cols + src_col; -- -- /* -- * before doing the transform, first we have to translate -- * source row,col for an origin in the center of s_image -- */ -- src_row = src_row * 2 - (s_image->num_rows - 1); -- src_col = src_col * 2 - (s_image->num_cols - 1); -- -- /* do the rotation transform */ -- if (ctx->rot_mode & IPU_ROT_BIT_90) { -- dst_col = -src_row; -- dst_row = src_col; -- } else { -- dst_col = src_col; -- dst_row = src_row; -- } -- -- /* apply flip */ -- if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -- dst_col = -dst_col; -- if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -- dst_row = -dst_row; -- -- dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -- chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -- -- /* -- * finally translate dest row,col using an origin in upper -- * left of d_image -- */ -- dst_row += d_image->num_rows - 1; -- dst_col += d_image->num_cols - 1; -- dst_row /= 2; -- dst_col /= 2; -- -- return dst_row * d_image->num_cols + dst_col; --} -- --/* -- * Fill the out_tile_map[] with transformed destination tile indeces. -- */ --static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_image *s_image = &ctx->in; -- unsigned int row, col, tile = 0; -- -- for (row = 0; row < s_image->num_rows; row++) { -- for (col = 0; col < s_image->num_cols; col++) { -- ctx->out_tile_map[tile] = -- transform_tile_index(ctx, row, col); -- tile++; -- } -- } --} -- --static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 H, top, y_stride, uv_stride; -- u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -- u32 y_row_off, y_col_off, y_off; -- u32 y_size, uv_size; -- -- /* setup some convenience vars */ -- H = image->base.pix.height; -- -- y_stride = image->stride; -- uv_stride = y_stride / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_stride *= 2; -- -- y_size = H * y_stride; -- uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -- -- for (row = 0; row < image->num_rows; row++) { -- top = image->tile[tile].top; -- y_row_off = top * y_stride; -- uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -- -- for (col = 0; col < image->num_cols; col++) { -- y_col_off = image->tile[tile].left; -- uv_col_off = y_col_off / fmt->uv_width_dec; -- if (fmt->uv_packed) -- uv_col_off *= 2; -- -- y_off = y_row_off + y_col_off; -- uv_off = uv_row_off + uv_col_off; -- -- u_off = y_size - y_off + uv_off; -- v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -- if (fmt->uv_swapped) { -- tmp = u_off; -- u_off = v_off; -- v_off = tmp; -- } -- -- image->tile[tile].offset = y_off; -- image->tile[tile].u_off = u_off; -- image->tile[tile++].v_off = v_off; -- -- if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "y_off %08x, u_off %08x, v_off %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- y_off, u_off, v_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- const struct ipu_image_pixfmt *fmt = image->fmt; -- unsigned int row, col, tile = 0; -- u32 bpp, stride, offset; -- u32 row_off, col_off; -- -- /* setup some convenience vars */ -- stride = image->stride; -- bpp = fmt->bpp; -- -- for (row = 0; row < image->num_rows; row++) { -- row_off = image->tile[tile].top * stride; -- -- for (col = 0; col < image->num_cols; col++) { -- col_off = (image->tile[tile].left * bpp) >> 3; -- -- offset = row_off + col_off; -- -- image->tile[tile].offset = offset; -- image->tile[tile].u_off = 0; -- image->tile[tile++].v_off = 0; -- -- if (offset & 0x7) { -- dev_err(priv->ipu->dev, -- "task %u: ctx %p: %s@[%d,%d]: " -- "phys %08x\n", -- chan->ic_task, ctx, -- image->type == IMAGE_CONVERT_IN ? -- "Input" : "Output", row, col, -- row_off + col_off); -- return -EINVAL; -- } -- } -- } -- -- return 0; --} -- --static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *image) --{ -- if (image->fmt->planar) -- return calc_tile_offsets_planar(ctx, image); -- -- return calc_tile_offsets_packed(ctx, image); --} -- --/* -- * Calculate the resizing ratio for the IC main processing section given input -- * size, fixed downsizing coefficient, and output size. -- * Either round to closest for the next tile's first pixel to minimize seams -- * and distortion (for all but right column / bottom row), or round down to -- * avoid sampling beyond the edges of the input image for this tile's last -- * pixel. -- * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -- */ --static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -- u32 output_size, bool allow_overshoot) --{ -- u32 downsized = input_size >> downsize_coeff; -- -- if (allow_overshoot) -- return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -- else -- return 8192 * (downsized - 1) / (output_size - 1); --} -- --/* -- * Slightly modify resize coefficients per tile to hide the bilinear -- * interpolator reset at tile borders, shifting the right / bottom edge -- * by up to a half input pixel. This removes noticeable seams between -- * tiles at higher upscaling factors. -- */ --static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_tile *in_tile, *out_tile; -- unsigned int col, row, tile_idx; -- unsigned int last_output; -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- bool closest = (col < ctx->in.num_cols - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -- u32 resized_width; -- u32 resize_coeff_h; -- u32 in_width; -- -- tile_idx = col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_width = out_tile->height; -- else -- resized_width = out_tile->width; -- -- resize_coeff_h = calc_resize_coeff(in_tile->width, -- ctx->downsize_coeff_h, -- resized_width, closest); -- -- dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -- __func__, col, resize_coeff_h); -- -- /* -- * With the horizontal scaling factor known, round up resized -- * width (output width or height) to burst size. -- */ -- resized_width = round_up(resized_width, 8); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized width and scaling coefficients. Round up to -- * burst size. -- */ -- last_output = resized_width - 1; -- if (closest && ((last_output * resize_coeff_h) % 8192)) -- last_output++; -- in_width = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -- << ctx->downsize_coeff_h, 8); -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->height = resized_width; -- else -- out_tile->width = resized_width; -- -- in_tile->width = in_width; -- } -- -- ctx->resize_coeffs_h[col] = resize_coeff_h; -- } -- -- for (row = 0; row < ctx->in.num_rows; row++) { -- bool closest = (row < ctx->in.num_rows - 1) && -- !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -- u32 resized_height; -- u32 resize_coeff_v; -- u32 in_height; -- -- tile_idx = row * ctx->in.num_cols; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- resized_height = out_tile->width; -- else -- resized_height = out_tile->height; -- -- resize_coeff_v = calc_resize_coeff(in_tile->height, -- ctx->downsize_coeff_v, -- resized_height, closest); -- -- dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -- __func__, row, resize_coeff_v); -- -- /* -- * With the vertical scaling factor known, round up resized -- * height (output width or height) to IDMAC limitations. -- */ -- resized_height = round_up(resized_height, 2); -- -- /* -- * Calculate input width from the last accessed input pixel -- * given resized height and scaling coefficients. Align to -- * IDMAC restrictions. -- */ -- last_output = resized_height - 1; -- if (closest && ((last_output * resize_coeff_v) % 8192)) -- last_output++; -- in_height = round_up( -- (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -- << ctx->downsize_coeff_v, 2); -- -- for (col = 0; col < ctx->in.num_cols; col++) { -- tile_idx = row * ctx->in.num_cols + col; -- in_tile = &ctx->in.tile[tile_idx]; -- out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- out_tile->width = resized_height; -- else -- out_tile->height = resized_height; -- -- in_tile->height = in_height; -- } -- -- ctx->resize_coeffs_v[row] = resize_coeff_v; -- } --} -- --/* -- * return the number of runs in given queue (pending_q or done_q) -- * for this context. hold irqlock when calling. -- */ --static int get_run_count(struct ipu_image_convert_ctx *ctx, -- struct list_head *q) --{ -- struct ipu_image_convert_run *run; -- int count = 0; -- -- lockdep_assert_held(&ctx->chan->irqlock); -- -- list_for_each_entry(run, q, list) { -- if (run->ctx == ctx) -- count++; -- } -- -- return count; --} -- --static void convert_stop(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -- __func__, chan->ic_task, ctx, run); -- -- /* disable IC tasks and the channels */ -- ipu_ic_task_disable(chan->ic); -- ipu_idmac_disable_channel(chan->in_chan); -- ipu_idmac_disable_channel(chan->out_chan); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_disable_channel(chan->rotation_in_chan); -- ipu_idmac_disable_channel(chan->rotation_out_chan); -- ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -- } -- -- ipu_ic_disable(chan->ic); --} -- --static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -- struct ipuv3_channel *channel, -- struct ipu_image_convert_image *image, -- enum ipu_rotate_mode rot_mode, -- bool rot_swap_width_height, -- unsigned int tile) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- unsigned int burst_size; -- u32 width, height, stride; -- dma_addr_t addr0, addr1 = 0; -- struct ipu_image tile_image; -- unsigned int tile_idx[2]; -- -- if (image->type == IMAGE_CONVERT_OUT) { -- tile_idx[0] = ctx->out_tile_map[tile]; -- tile_idx[1] = ctx->out_tile_map[1]; -- } else { -- tile_idx[0] = tile; -- tile_idx[1] = 1; -- } -- -- if (rot_swap_width_height) { -- width = image->tile[tile_idx[0]].height; -- height = image->tile[tile_idx[0]].width; -- stride = image->tile[tile_idx[0]].rot_stride; -- addr0 = ctx->rot_intermediate[0].phys; -- if (ctx->double_buffering) -- addr1 = ctx->rot_intermediate[1].phys; -- } else { -- width = image->tile[tile_idx[0]].width; -- height = image->tile[tile_idx[0]].height; -- stride = image->stride; -- addr0 = image->base.phys0 + -- image->tile[tile_idx[0]].offset; -- if (ctx->double_buffering) -- addr1 = image->base.phys0 + -- image->tile[tile_idx[1]].offset; -- } -- -- ipu_cpmem_zero(channel); -- -- memset(&tile_image, 0, sizeof(tile_image)); -- tile_image.pix.width = tile_image.rect.width = width; -- tile_image.pix.height = tile_image.rect.height = height; -- tile_image.pix.bytesperline = stride; -- tile_image.pix.pixelformat = image->fmt->fourcc; -- tile_image.phys0 = addr0; -- tile_image.phys1 = addr1; -- if (image->fmt->planar && !rot_swap_width_height) { -- tile_image.u_offset = image->tile[tile_idx[0]].u_off; -- tile_image.v_offset = image->tile[tile_idx[0]].v_off; -- } -- -- ipu_cpmem_set_image(channel, &tile_image); -- -- if (rot_mode) -- ipu_cpmem_set_rotation(channel, rot_mode); -- -- /* -- * Skip writing U and V components to odd rows in the output -- * channels for planar 4:2:0. -- */ -- if ((channel == chan->out_chan || -- channel == chan->rotation_out_chan) && -- image->fmt->planar && image->fmt->uv_height_dec == 2) -- ipu_cpmem_skip_odd_chroma_rows(channel); -- -- if (channel == chan->rotation_in_chan || -- channel == chan->rotation_out_chan) { -- burst_size = 8; -- ipu_cpmem_set_block_mode(channel); -- } else -- burst_size = (width % 16) ? 8 : 16; -- -- ipu_cpmem_set_burstsize(channel, burst_size); -- -- ipu_ic_task_idma_init(chan->ic, channel, width, height, -- burst_size, rot_mode); -- -- /* -- * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -- * only do this when there is no PRG present. -- */ -- if (!channel->ipu->prg_priv) -- ipu_cpmem_set_axi_id(channel, 1); -- -- ipu_idmac_set_double_buffer(channel, ctx->double_buffering); --} -- --static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- unsigned int dst_tile = ctx->out_tile_map[tile]; -- unsigned int dest_width, dest_height; -- unsigned int col, row; -- u32 rsc; -- int ret; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -- __func__, chan->ic_task, ctx, run, tile, dst_tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* swap width/height for resizer */ -- dest_width = d_image->tile[dst_tile].height; -- dest_height = d_image->tile[dst_tile].width; -- } else { -- dest_width = d_image->tile[dst_tile].width; -- dest_height = d_image->tile[dst_tile].height; -- } -- -- row = tile / s_image->num_cols; -- col = tile % s_image->num_cols; -- -- rsc = (ctx->downsize_coeff_v << 30) | -- (ctx->resize_coeffs_v[row] << 16) | -- (ctx->downsize_coeff_h << 14) | -- (ctx->resize_coeffs_h[col]); -- -- dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -- __func__, s_image->tile[tile].width, -- s_image->tile[tile].height, dest_width, dest_height, rsc); -- -- /* setup the IC resizer and CSC */ -- ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -- s_image->tile[tile].width, -- s_image->tile[tile].height, -- dest_width, -- dest_height, -- rsc); -- if (ret) { -- dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -- return ret; -- } -- -- /* init the source MEM-->IC PP IDMAC channel */ -- init_idmac_channel(ctx, chan->in_chan, s_image, -- IPU_ROTATE_NONE, false, tile); -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* init the IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- IPU_ROTATE_NONE, true, tile); -- -- /* init the MEM-->IC PP ROT IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -- ctx->rot_mode, true, tile); -- -- /* init the destination IC PP ROT-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -- IPU_ROTATE_NONE, false, tile); -- -- /* now link IC PP-->MEM to MEM-->IC PP ROT */ -- ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -- } else { -- /* init the destination IC PP-->MEM IDMAC channel */ -- init_idmac_channel(ctx, chan->out_chan, d_image, -- ctx->rot_mode, false, tile); -- } -- -- /* enable the IC */ -- ipu_ic_enable(chan->ic); -- -- /* set buffers ready */ -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(chan->out_chan, 0); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -- if (ctx->double_buffering) { -- ipu_idmac_select_buffer(chan->in_chan, 1); -- ipu_idmac_select_buffer(chan->out_chan, 1); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) -- ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -- } -- -- /* enable the channels! */ -- ipu_idmac_enable_channel(chan->in_chan); -- ipu_idmac_enable_channel(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_idmac_enable_channel(chan->rotation_in_chan); -- ipu_idmac_enable_channel(chan->rotation_out_chan); -- } -- -- ipu_ic_task_enable(chan->ic); -- -- ipu_cpmem_dump(chan->in_chan); -- ipu_cpmem_dump(chan->out_chan); -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- ipu_cpmem_dump(chan->rotation_in_chan); -- ipu_cpmem_dump(chan->rotation_out_chan); -- } -- -- ipu_dump(priv->ipu); -- -- return 0; --} -- --/* hold irqlock when calling */ --static int do_run(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- -- lockdep_assert_held(&chan->irqlock); -- -- ctx->in.base.phys0 = run->in_phys; -- ctx->out.base.phys0 = run->out_phys; -- -- ctx->cur_buf_num = 0; -- ctx->next_tile = 1; -- -- /* remove run from pending_q and set as current */ -- list_del(&run->list); -- chan->current_run = run; -- -- return convert_start(run, 0); --} -- --/* hold irqlock when calling */ --static void run_next(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *tmp; -- int ret; -- -- lockdep_assert_held(&chan->irqlock); -- -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- /* skip contexts that are aborting */ -- if (run->ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: skipping aborting ctx %p run %p\n", -- __func__, chan->ic_task, run->ctx, run); -- continue; -- } -- -- ret = do_run(run); -- if (!ret) -- break; -- -- /* -- * something went wrong with start, add the run -- * to done q and continue to the next run in the -- * pending q. -- */ -- run->status = ret; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- } --} -- --static void empty_done_q(struct ipu_image_convert_chan *chan) --{ -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- while (!list_empty(&chan->done_q)) { -- run = list_entry(chan->done_q.next, -- struct ipu_image_convert_run, -- list); -- -- list_del(&run->list); -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: completing ctx %p run %p with %d\n", -- __func__, chan->ic_task, run->ctx, run, run->status); -- -- /* call the completion callback and free the run */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- run->ctx->complete(run, run->ctx->complete_context); -- spin_lock_irqsave(&chan->irqlock, flags); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); --} -- --/* -- * the bottom half thread clears out the done_q, calling the -- * completion handler for each. -- */ --static irqreturn_t do_bh(int irq, void *dev_id) --{ -- struct ipu_image_convert_chan *chan = dev_id; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -- chan->ic_task); -- -- empty_done_q(chan); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* -- * the done_q is cleared out, signal any contexts -- * that are aborting that abort can complete. -- */ -- list_for_each_entry(ctx, &chan->ctx_list, list) { -- if (ctx->aborting) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: signaling abort for ctx %p\n", -- __func__, chan->ic_task, ctx); -- complete_all(&ctx->aborted); -- } -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -- chan->ic_task); -- -- return IRQ_HANDLED; --} -- --static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) --{ -- unsigned int cur_tile = ctx->next_tile - 1; -- unsigned int next_tile = ctx->next_tile; -- -- if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -- ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -- ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -- ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -- ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -- ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -- ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -- ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -- return true; -- -- return false; --} -- --/* hold irqlock when calling */ --static irqreturn_t do_irq(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_ctx *ctx = run->ctx; -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_tile *src_tile, *dst_tile; -- struct ipu_image_convert_image *s_image = &ctx->in; -- struct ipu_image_convert_image *d_image = &ctx->out; -- struct ipuv3_channel *outch; -- unsigned int dst_idx; -- -- lockdep_assert_held(&chan->irqlock); -- -- outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -- chan->rotation_out_chan : chan->out_chan; -- -- /* -- * It is difficult to stop the channel DMA before the channels -- * enter the paused state. Without double-buffering the channels -- * are always in a paused state when the EOF irq occurs, so it -- * is safe to stop the channels now. For double-buffering we -- * just ignore the abort until the operation completes, when it -- * is safe to shut down. -- */ -- if (ctx->aborting && !ctx->double_buffering) { -- convert_stop(run); -- run->status = -EIO; -- goto done; -- } -- -- if (ctx->next_tile == ctx->num_tiles) { -- /* -- * the conversion is complete -- */ -- convert_stop(run); -- run->status = 0; -- goto done; -- } -- -- /* -- * not done, place the next tile buffers. -- */ -- if (!ctx->double_buffering) { -- if (ic_settings_changed(ctx)) { -- convert_stop(run); -- convert_start(run, ctx->next_tile); -- } else { -- src_tile = &s_image->tile[ctx->next_tile]; -- dst_idx = ctx->out_tile_map[ctx->next_tile]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, 0, -- s_image->base.phys0 + -- src_tile->offset); -- ipu_cpmem_set_buffer(outch, 0, -- d_image->base.phys0 + -- dst_tile->offset); -- if (s_image->fmt->planar) -- ipu_cpmem_set_uv_offset(chan->in_chan, -- src_tile->u_off, -- src_tile->v_off); -- if (d_image->fmt->planar) -- ipu_cpmem_set_uv_offset(outch, -- dst_tile->u_off, -- dst_tile->v_off); -- -- ipu_idmac_select_buffer(chan->in_chan, 0); -- ipu_idmac_select_buffer(outch, 0); -- } -- } else if (ctx->next_tile < ctx->num_tiles - 1) { -- -- src_tile = &s_image->tile[ctx->next_tile + 1]; -- dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -- dst_tile = &d_image->tile[dst_idx]; -- -- ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -- s_image->base.phys0 + src_tile->offset); -- ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -- d_image->base.phys0 + dst_tile->offset); -- -- ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -- ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -- -- ctx->cur_buf_num ^= 1; -- } -- -- ctx->next_tile++; -- return IRQ_HANDLED; --done: -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- return IRQ_WAKE_THREAD; --} -- --static irqreturn_t norotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this is a rotation operation, just ignore */ -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --static irqreturn_t rotate_irq(int irq, void *data) --{ -- struct ipu_image_convert_chan *chan = data; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- irqreturn_t ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* get current run and its context */ -- run = chan->current_run; -- if (!run) { -- ret = IRQ_NONE; -- goto out; -- } -- -- ctx = run->ctx; -- -- if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -- /* this was NOT a rotation operation, shouldn't happen */ -- dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return IRQ_HANDLED; -- } -- -- ret = do_irq(run); --out: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} -- --/* -- * try to force the completion of runs for this ctx. Called when -- * abort wait times out in ipu_image_convert_abort(). -- */ --static void force_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_run *run; -- unsigned long flags; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- run = chan->current_run; -- if (run && run->ctx == ctx) { -- convert_stop(run); -- run->status = -EIO; -- list_add_tail(&run->list, &chan->done_q); -- chan->current_run = NULL; -- run_next(chan); -- } -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- empty_done_q(chan); --} -- --static void release_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- if (chan->out_eof_irq >= 0) -- free_irq(chan->out_eof_irq, chan); -- if (chan->rot_out_eof_irq >= 0) -- free_irq(chan->rot_out_eof_irq, chan); -- -- if (!IS_ERR_OR_NULL(chan->in_chan)) -- ipu_idmac_put(chan->in_chan); -- if (!IS_ERR_OR_NULL(chan->out_chan)) -- ipu_idmac_put(chan->out_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -- ipu_idmac_put(chan->rotation_in_chan); -- if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -- ipu_idmac_put(chan->rotation_out_chan); -- if (!IS_ERR_OR_NULL(chan->ic)) -- ipu_ic_put(chan->ic); -- -- chan->in_chan = chan->out_chan = chan->rotation_in_chan = -- chan->rotation_out_chan = NULL; -- chan->out_eof_irq = chan->rot_out_eof_irq = -1; --} -- --static int get_ipu_resources(struct ipu_image_convert_chan *chan) --{ -- const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -- struct ipu_image_convert_priv *priv = chan->priv; -- int ret; -- -- /* get IC */ -- chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -- if (IS_ERR(chan->ic)) { -- dev_err(priv->ipu->dev, "could not acquire IC\n"); -- ret = PTR_ERR(chan->ic); -- goto err; -- } -- -- /* get IDMAC channels */ -- chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -- chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -- if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -- dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -- chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -- if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -- dev_err(priv->ipu->dev, -- "could not acquire idmac rotation channels\n"); -- ret = -EBUSY; -- goto err; -- } -- -- /* acquire the EOF interrupts */ -- chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->out_eof_irq); -- chan->out_eof_irq = -1; -- goto err; -- } -- -- chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -- chan->rotation_out_chan, -- IPU_IRQ_EOF); -- -- ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -- 0, "ipu-ic", chan); -- if (ret < 0) { -- dev_err(priv->ipu->dev, "could not acquire irq %d\n", -- chan->rot_out_eof_irq); -- chan->rot_out_eof_irq = -1; -- goto err; -- } -- -- return 0; --err: -- release_ipu_resources(chan); -- return ret; --} -- --static int fill_image(struct ipu_image_convert_ctx *ctx, -- struct ipu_image_convert_image *ic_image, -- struct ipu_image *image, -- enum ipu_image_convert_type type) --{ -- struct ipu_image_convert_priv *priv = ctx->chan->priv; -- -- ic_image->base = *image; -- ic_image->type = type; -- -- ic_image->fmt = get_format(image->pix.pixelformat); -- if (!ic_image->fmt) { -- dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -- type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -- return -EINVAL; -- } -- -- if (ic_image->fmt->planar) -- ic_image->stride = ic_image->base.pix.width; -- else -- ic_image->stride = ic_image->base.pix.bytesperline; -- -- return 0; --} -- --/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ --static unsigned int clamp_align(unsigned int x, unsigned int min, -- unsigned int max, unsigned int align) --{ -- /* Bits that must be zero to be aligned */ -- unsigned int mask = ~((1 << align) - 1); -- -- /* Clamp to aligned min and max */ -- x = clamp(x, (min + ~mask) & mask, max & mask); -- -- /* Round to nearest aligned value */ -- if (align) -- x = (x + (1 << (align - 1))) & mask; -- -- return x; --} -- --/* Adjusts input/output images to IPU restrictions */ --void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- const struct ipu_image_pixfmt *infmt, *outfmt; -- u32 w_align_out, h_align_out; -- u32 w_align_in, h_align_in; -- -- infmt = get_format(in->pix.pixelformat); -- outfmt = get_format(out->pix.pixelformat); -- -- /* set some default pixel formats if needed */ -- if (!infmt) { -- in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- infmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- if (!outfmt) { -- out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -- outfmt = get_format(V4L2_PIX_FMT_RGB24); -- } -- -- /* image converter does not handle fields */ -- in->pix.field = out->pix.field = V4L2_FIELD_NONE; -- -- /* resizer cannot downsize more than 4:1 */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.width / 4); -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.height / 4); -- } else { -- out->pix.width = max_t(__u32, out->pix.width, -- in->pix.width / 4); -- out->pix.height = max_t(__u32, out->pix.height, -- in->pix.height / 4); -- } -- -- /* align input width/height */ -- w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -- rot_mode)); -- in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -- w_align_in); -- in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -- h_align_in); -- -- /* align output width/height */ -- w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -- rot_mode)); -- out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -- w_align_out); -- out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -- h_align_out); -- -- /* set input/output strides and image sizes */ -- in->pix.bytesperline = infmt->planar ? -- clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -- w_align_in) : -- clamp_align((in->pix.width * infmt->bpp) >> 3, -- ((2 << w_align_in) * infmt->bpp) >> 3, -- (MAX_W * infmt->bpp) >> 3, -- w_align_in); -- in->pix.sizeimage = infmt->planar ? -- (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -- in->pix.height * in->pix.bytesperline; -- out->pix.bytesperline = outfmt->planar ? out->pix.width : -- (out->pix.width * outfmt->bpp) >> 3; -- out->pix.sizeimage = outfmt->planar ? -- (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -- out->pix.height * out->pix.bytesperline; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -- --/* -- * this is used by ipu_image_convert_prepare() to verify set input and -- * output images are valid before starting the conversion. Clients can -- * also call it before calling ipu_image_convert_prepare(). -- */ --int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image testin, testout; -- -- testin = *in; -- testout = *out; -- -- ipu_image_convert_adjust(&testin, &testout, rot_mode); -- -- if (testin.pix.width != in->pix.width || -- testin.pix.height != in->pix.height || -- testout.pix.width != out->pix.width || -- testout.pix.height != out->pix.height) -- return -EINVAL; -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -- --/* -- * Call ipu_image_convert_prepare() to prepare for the conversion of -- * given images and rotation mode. Returns a new conversion context. -- */ --struct ipu_image_convert_ctx * --ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -- struct ipu_image_convert_image *s_image, *d_image; -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- unsigned int i; -- bool get_res; -- int ret; -- -- if (!in || !out || !complete || -- (ic_task != IC_TASK_VIEWFINDER && -- ic_task != IC_TASK_POST_PROCESSOR)) -- return ERR_PTR(-EINVAL); -- -- /* verify the in/out images before continuing */ -- ret = ipu_image_convert_verify(in, out, rot_mode); -- if (ret) { -- dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -- __func__); -- return ERR_PTR(ret); -- } -- -- chan = &priv->chan[ic_task]; -- -- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -- if (!ctx) -- return ERR_PTR(-ENOMEM); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- ctx->chan = chan; -- init_completion(&ctx->aborted); -- -- ctx->rot_mode = rot_mode; -- -- /* Sets ctx->in.num_rows/cols as well */ -- ret = calc_image_resize_coefficients(ctx, in, out); -- if (ret) -- goto out_free; -- -- s_image = &ctx->in; -- d_image = &ctx->out; -- -- /* set tiling and rotation */ -- if (ipu_rot_mode_is_irt(rot_mode)) { -- d_image->num_rows = s_image->num_cols; -- d_image->num_cols = s_image->num_rows; -- } else { -- d_image->num_rows = s_image->num_rows; -- d_image->num_cols = s_image->num_cols; -- } -- -- ctx->num_tiles = d_image->num_cols * d_image->num_rows; -- -- ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -- if (ret) -- goto out_free; -- ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -- if (ret) -- goto out_free; -- -- calc_out_tile_map(ctx); -- -- find_seams(ctx, s_image, d_image); -- -- ret = calc_tile_dimensions(ctx, s_image); -- if (ret) -- goto out_free; -- -- ret = calc_tile_offsets(ctx, s_image); -- if (ret) -- goto out_free; -- -- calc_tile_dimensions(ctx, d_image); -- ret = calc_tile_offsets(ctx, d_image); -- if (ret) -- goto out_free; -- -- calc_tile_resize_coefficients(ctx); -- -- ret = ipu_ic_calc_csc(&ctx->csc, -- s_image->base.pix.ycbcr_enc, -- s_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -- d_image->base.pix.ycbcr_enc, -- d_image->base.pix.quantization, -- ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -- if (ret) -- goto out_free; -- -- dump_format(ctx, s_image); -- dump_format(ctx, d_image); -- -- ctx->complete = complete; -- ctx->complete_context = complete_context; -- -- /* -- * Can we use double-buffering for this operation? If there is -- * only one tile (the whole image can be converted in a single -- * operation) there's no point in using double-buffering. Also, -- * the IPU's IDMAC channels allow only a single U and V plane -- * offset shared between both buffers, but these offsets change -- * for every tile, and therefore would have to be updated for -- * each buffer which is not possible. So double-buffering is -- * impossible when either the source or destination images are -- * a planar format (YUV420, YUV422P, etc.). Further, differently -- * sized tiles or different resizing coefficients per tile -- * prevent double-buffering as well. -- */ -- ctx->double_buffering = (ctx->num_tiles > 1 && -- !s_image->fmt->planar && -- !d_image->fmt->planar); -- for (i = 1; i < ctx->num_tiles; i++) { -- if (ctx->in.tile[i].width != ctx->in.tile[0].width || -- ctx->in.tile[i].height != ctx->in.tile[0].height || -- ctx->out.tile[i].width != ctx->out.tile[0].width || -- ctx->out.tile[i].height != ctx->out.tile[0].height) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_cols; i++) { -- if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- for (i = 1; i < ctx->in.num_rows; i++) { -- if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -- ctx->double_buffering = false; -- break; -- } -- } -- -- if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -- unsigned long intermediate_size = d_image->tile[0].size; -- -- for (i = 1; i < ctx->num_tiles; i++) { -- if (d_image->tile[i].size > intermediate_size) -- intermediate_size = d_image->tile[i].size; -- } -- -- ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -- intermediate_size); -- if (ret) -- goto out_free; -- if (ctx->double_buffering) { -- ret = alloc_dma_buf(priv, -- &ctx->rot_intermediate[1], -- intermediate_size); -- if (ret) -- goto out_free_dmabuf0; -- } -- } -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- get_res = list_empty(&chan->ctx_list); -- -- list_add_tail(&ctx->list, &chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (get_res) { -- ret = get_ipu_resources(chan); -- if (ret) -- goto out_free_dmabuf1; -- } -- -- return ctx; -- --out_free_dmabuf1: -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- spin_lock_irqsave(&chan->irqlock, flags); -- list_del(&ctx->list); -- spin_unlock_irqrestore(&chan->irqlock, flags); --out_free_dmabuf0: -- free_dma_buf(priv, &ctx->rot_intermediate[0]); --out_free: -- kfree(ctx); -- return ERR_PTR(ret); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -- --/* -- * Carry out a single image conversion run. Only the physaddr's of the input -- * and output image buffers are needed. The conversion context must have -- * been created previously with ipu_image_convert_prepare(). -- */ --int ipu_image_convert_queue(struct ipu_image_convert_run *run) --{ -- struct ipu_image_convert_chan *chan; -- struct ipu_image_convert_priv *priv; -- struct ipu_image_convert_ctx *ctx; -- unsigned long flags; -- int ret = 0; -- -- if (!run || !run->ctx || !run->in_phys || !run->out_phys) -- return -EINVAL; -- -- ctx = run->ctx; -- chan = ctx->chan; -- priv = chan->priv; -- -- dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -- chan->ic_task, ctx, run); -- -- INIT_LIST_HEAD(&run->list); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- if (ctx->aborting) { -- ret = -EIO; -- goto unlock; -- } -- -- list_add_tail(&run->list, &chan->pending_q); -- -- if (!chan->current_run) { -- ret = do_run(run); -- if (ret) -- chan->current_run = NULL; -- } --unlock: -- spin_unlock_irqrestore(&chan->irqlock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -- --/* Abort any active or pending conversions for this context */ --static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- struct ipu_image_convert_run *run, *active_run, *tmp; -- unsigned long flags; -- int run_count, ret; -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- /* move all remaining pending runs in this context to done_q */ -- list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -- if (run->ctx != ctx) -- continue; -- run->status = -EIO; -- list_move_tail(&run->list, &chan->done_q); -- } -- -- run_count = get_run_count(ctx, &chan->done_q); -- active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -- chan->current_run : NULL; -- -- if (active_run) -- reinit_completion(&ctx->aborted); -- -- ctx->aborting = true; -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (!run_count && !active_run) { -- dev_dbg(priv->ipu->dev, -- "%s: task %u: no abort needed for ctx %p\n", -- __func__, chan->ic_task, ctx); -- return; -- } -- -- if (!active_run) { -- empty_done_q(chan); -- return; -- } -- -- dev_dbg(priv->ipu->dev, -- "%s: task %u: wait for completion: %d runs\n", -- __func__, chan->ic_task, run_count); -- -- ret = wait_for_completion_timeout(&ctx->aborted, -- msecs_to_jiffies(10000)); -- if (ret == 0) { -- dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -- force_abort(ctx); -- } --} -- --void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) --{ -- __ipu_image_convert_abort(ctx); -- ctx->aborting = false; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -- --/* Unprepare image conversion context */ --void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) --{ -- struct ipu_image_convert_chan *chan = ctx->chan; -- struct ipu_image_convert_priv *priv = chan->priv; -- unsigned long flags; -- bool put_res; -- -- /* make sure no runs are hanging around */ -- __ipu_image_convert_abort(ctx); -- -- dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -- chan->ic_task, ctx); -- -- spin_lock_irqsave(&chan->irqlock, flags); -- -- list_del(&ctx->list); -- -- put_res = list_empty(&chan->ctx_list); -- -- spin_unlock_irqrestore(&chan->irqlock, flags); -- -- if (put_res) -- release_ipu_resources(chan); -- -- free_dma_buf(priv, &ctx->rot_intermediate[1]); -- free_dma_buf(priv, &ctx->rot_intermediate[0]); -- -- kfree(ctx); --} --EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -- --/* -- * "Canned" asynchronous single image conversion. Allocates and returns -- * a new conversion run. On successful return the caller must free the -- * run and call ipu_image_convert_unprepare() after conversion completes. -- */ --struct ipu_image_convert_run * --ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode, -- ipu_image_convert_cb_t complete, -- void *complete_context) --{ -- struct ipu_image_convert_ctx *ctx; -- struct ipu_image_convert_run *run; -- int ret; -- -- ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -- complete, complete_context); -- if (IS_ERR(ctx)) -- return ERR_CAST(ctx); -- -- run = kzalloc(sizeof(*run), GFP_KERNEL); -- if (!run) { -- ipu_image_convert_unprepare(ctx); -- return ERR_PTR(-ENOMEM); -- } -- -- run->ctx = ctx; -- run->in_phys = in->phys0; -- run->out_phys = out->phys0; -- -- ret = ipu_image_convert_queue(run); -- if (ret) { -- ipu_image_convert_unprepare(ctx); -- kfree(run); -- return ERR_PTR(ret); -- } -- -- return run; --} --EXPORT_SYMBOL_GPL(ipu_image_convert); -- --/* "Canned" synchronous single image conversion */ --static void image_convert_sync_complete(struct ipu_image_convert_run *run, -- void *data) --{ -- struct completion *comp = data; -- -- complete(comp); --} -- --int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -- struct ipu_image *in, struct ipu_image *out, -- enum ipu_rotate_mode rot_mode) --{ -- struct ipu_image_convert_run *run; -- struct completion comp; -- int ret; -- -- init_completion(&comp); -- -- run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -- image_convert_sync_complete, &comp); -- if (IS_ERR(run)) -- return PTR_ERR(run); -- -- ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -- ret = (ret == 0) ? -ETIMEDOUT : 0; -- -- ipu_image_convert_unprepare(run->ctx); -- kfree(run); -- -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) --{ -- struct ipu_image_convert_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->image_convert_priv = priv; -- priv->ipu = ipu; -- -- for (i = 0; i < IC_NUM_TASKS; i++) { -- struct ipu_image_convert_chan *chan = &priv->chan[i]; -- -- chan->ic_task = i; -- chan->priv = priv; -- chan->dma_ch = &image_convert_dma_chan[i]; -- chan->out_eof_irq = -1; -- chan->rot_out_eof_irq = -1; -- -- spin_lock_init(&chan->irqlock); -- INIT_LIST_HEAD(&chan->ctx_list); -- INIT_LIST_HEAD(&chan->pending_q); -- INIT_LIST_HEAD(&chan->done_q); -- } -- -- return 0; --} -- --void ipu_image_convert_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-pre.c -+++ /dev/null -@@ -1,346 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/genalloc.h> --#include <linux/module.h> --#include <linux/of.h> --#include <linux/platform_device.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --#define IPU_PRE_MAX_WIDTH 2048 --#define IPU_PRE_NUM_SCANLINES 8 -- --#define IPU_PRE_CTRL 0x000 --#define IPU_PRE_CTRL_SET 0x004 --#define IPU_PRE_CTRL_ENABLE (1 << 0) --#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) --#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) --#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) --#define IPU_PRE_CTRL_VFLIP (1 << 5) --#define IPU_PRE_CTRL_SO (1 << 6) --#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) --#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) --#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) --#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) --#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) --#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) --#define IPU_PRE_CTRL_CLKGATE (1 << 30) --#define IPU_PRE_CTRL_SFTRST (1 << 31) -- --#define IPU_PRE_CUR_BUF 0x030 -- --#define IPU_PRE_NEXT_BUF 0x040 -- --#define IPU_PRE_TPR_CTRL 0x070 --#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5) --#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6) -- --#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 --#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) --#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) --#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) --#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) --#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) --#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) --#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -- --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 --#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) --#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_CTRL 0x110 --#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) --#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) --#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -- --#define IPU_PRE_STORE_ENG_STATUS 0x120 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff --#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) --#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -- --#define IPU_PRE_STORE_ENG_SIZE 0x130 --#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) --#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -- --#define IPU_PRE_STORE_ENG_PITCH 0x140 --#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -- --#define IPU_PRE_STORE_ENG_ADDR 0x150 -- --struct ipu_pre { -- struct list_head list; -- struct device *dev; -- -- void __iomem *regs; -- struct clk *clk_axi; -- struct gen_pool *iram; -- -- dma_addr_t buffer_paddr; -- void *buffer_virt; -- bool in_use; -- unsigned int safe_window_end; -- unsigned int last_bufaddr; --}; -- --static DEFINE_MUTEX(ipu_pre_list_mutex); --static LIST_HEAD(ipu_pre_list); --static int available_pres; -- --int ipu_pre_get_available_count(void) --{ -- return available_pres; --} -- --struct ipu_pre * --ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) --{ -- struct device_node *pre_node = of_parse_phandle(dev->of_node, -- name, index); -- struct ipu_pre *pre; -- -- mutex_lock(&ipu_pre_list_mutex); -- list_for_each_entry(pre, &ipu_pre_list, list) { -- if (pre_node == pre->dev->of_node) { -- mutex_unlock(&ipu_pre_list_mutex); -- device_link_add(dev, pre->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- of_node_put(pre_node); -- return pre; -- } -- } -- mutex_unlock(&ipu_pre_list_mutex); -- -- of_node_put(pre_node); -- -- return NULL; --} -- --int ipu_pre_get(struct ipu_pre *pre) --{ -- u32 val; -- -- if (pre->in_use) -- return -EBUSY; -- -- /* first get the engine out of reset and remove clock gating */ -- writel(0, pre->regs + IPU_PRE_CTRL); -- -- /* init defaults that should be applied to all streams */ -- val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -- IPU_PRE_CTRL_HANDSHAKE_EN | -- IPU_PRE_CTRL_TPR_REST_SEL | -- IPU_PRE_CTRL_SDW_UPDATE; -- writel(val, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = true; -- return 0; --} -- --void ipu_pre_put(struct ipu_pre *pre) --{ -- writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -- -- pre->in_use = false; --} -- --void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -- unsigned int height, unsigned int stride, u32 format, -- uint64_t modifier, unsigned int bufaddr) --{ -- const struct drm_format_info *info = drm_format_info(format); -- u32 active_bpp = info->cpp[0] >> 1; -- u32 val; -- -- /* calculate safe window for ctrl register updates */ -- if (modifier == DRM_FORMAT_MOD_LINEAR) -- pre->safe_window_end = height - 2; -- else -- pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1; -- -- writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); -- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -- pre->last_bufaddr = bufaddr; -- -- val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -- IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -- IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -- IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -- -- val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -- IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -- -- val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -- -- val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -- IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -- IPU_PRE_STORE_ENG_CTRL_STORE_EN; -- writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -- -- val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -- IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -- -- val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -- writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -- -- writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); -- -- val = readl(pre->regs + IPU_PRE_TPR_CTRL); -- val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK; -- if (modifier != DRM_FORMAT_MOD_LINEAR) { -- /* only support single buffer formats for now */ -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF; -- if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED; -- if (info->cpp[0] == 2) -- val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT; -- } -- writel(val, pre->regs + IPU_PRE_TPR_CTRL); -- -- val = readl(pre->regs + IPU_PRE_CTRL); -- val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | -- IPU_PRE_CTRL_SDW_UPDATE; -- if (modifier == DRM_FORMAT_MOD_LINEAR) -- val &= ~IPU_PRE_CTRL_BLOCK_EN; -- else -- val |= IPU_PRE_CTRL_BLOCK_EN; -- writel(val, pre->regs + IPU_PRE_CTRL); --} -- --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) --{ -- unsigned long timeout = jiffies + msecs_to_jiffies(5); -- unsigned short current_yblock; -- u32 val; -- -- if (bufaddr == pre->last_bufaddr) -- return; -- -- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -- pre->last_bufaddr = bufaddr; -- -- do { -- if (time_after(jiffies, timeout)) { -- dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -- return; -- } -- -- val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -- current_yblock = -- (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -- IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -- } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -- -- writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); --} -- --bool ipu_pre_update_pending(struct ipu_pre *pre) --{ -- return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -- IPU_PRE_CTRL_SDW_UPDATE); --} -- --u32 ipu_pre_get_baddr(struct ipu_pre *pre) --{ -- return (u32)pre->buffer_paddr; --} -- --static int ipu_pre_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_pre *pre; -- -- pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -- if (!pre) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- pre->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(pre->regs)) -- return PTR_ERR(pre->regs); -- -- pre->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(pre->clk_axi)) -- return PTR_ERR(pre->clk_axi); -- -- pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -- if (!pre->iram) -- return -EPROBE_DEFER; -- -- /* -- * Allocate IRAM buffer with maximum size. This could be made dynamic, -- * but as there is no other user of this IRAM region and we can fit all -- * max sized buffers into it, there is no need yet. -- */ -- pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -- IPU_PRE_NUM_SCANLINES * 4, -- &pre->buffer_paddr); -- if (!pre->buffer_virt) -- return -ENOMEM; -- -- clk_prepare_enable(pre->clk_axi); -- -- pre->dev = dev; -- platform_set_drvdata(pdev, pre); -- mutex_lock(&ipu_pre_list_mutex); -- list_add(&pre->list, &ipu_pre_list); -- available_pres++; -- mutex_unlock(&ipu_pre_list_mutex); -- -- return 0; --} -- --static int ipu_pre_remove(struct platform_device *pdev) --{ -- struct ipu_pre *pre = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_pre_list_mutex); -- list_del(&pre->list); -- available_pres--; -- mutex_unlock(&ipu_pre_list_mutex); -- -- clk_disable_unprepare(pre->clk_axi); -- -- if (pre->buffer_virt) -- gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -- IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -- return 0; --} -- --static const struct of_device_id ipu_pre_dt_ids[] = { -- { .compatible = "fsl,imx6qp-pre", }, -- { /* sentinel */ }, --}; -- --struct platform_driver ipu_pre_drv = { -- .probe = ipu_pre_probe, -- .remove = ipu_pre_remove, -- .driver = { -- .name = "imx-ipu-pre", -- .of_match_table = ipu_pre_dt_ids, -- }, --}; ---- a/drivers/gpu/imx/ipu-v3/ipu-prg.c -+++ /dev/null -@@ -1,483 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -- */ -- --#include <drm/drm_fourcc.h> --#include <linux/clk.h> --#include <linux/err.h> --#include <linux/iopoll.h> --#include <linux/mfd/syscon.h> --#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> --#include <linux/module.h> --#include <linux/of.h> --#include <linux/platform_device.h> --#include <linux/pm_runtime.h> --#include <linux/regmap.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --#define IPU_PRG_CTL 0x00 --#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) --#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 --#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) --#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) --#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) --#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) --#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) --#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) --#define IPU_PRG_CTL_SOFTRST (1 << 30) --#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -- --#define IPU_PRG_STATUS 0x04 --#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) --#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -- --#define IPU_PRG_QOS 0x08 --#define IPU_PRG_QOS_ARID_MASK 0xf --#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -- --#define IPU_PRG_REG_UPDATE 0x0c --#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -- --#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) --#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -- --#define IPU_PRG_CROP_LINE 0x1c -- --#define IPU_PRG_THD 0x20 -- --#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -- --#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -- --#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -- --#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) --#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 --#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff --#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -- --struct ipu_prg_channel { -- bool enabled; -- int used_pre; --}; -- --struct ipu_prg { -- struct list_head list; -- struct device *dev; -- int id; -- -- void __iomem *regs; -- struct clk *clk_ipg, *clk_axi; -- struct regmap *iomuxc_gpr; -- struct ipu_pre *pres[3]; -- -- struct ipu_prg_channel chan[3]; --}; -- --static DEFINE_MUTEX(ipu_prg_list_mutex); --static LIST_HEAD(ipu_prg_list); -- --struct ipu_prg * --ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) --{ -- struct device_node *prg_node = of_parse_phandle(dev->of_node, -- name, 0); -- struct ipu_prg *prg; -- -- mutex_lock(&ipu_prg_list_mutex); -- list_for_each_entry(prg, &ipu_prg_list, list) { -- if (prg_node == prg->dev->of_node) { -- mutex_unlock(&ipu_prg_list_mutex); -- device_link_add(dev, prg->dev, -- DL_FLAG_AUTOREMOVE_CONSUMER); -- prg->id = ipu_id; -- of_node_put(prg_node); -- return prg; -- } -- } -- mutex_unlock(&ipu_prg_list_mutex); -- -- of_node_put(prg_node); -- -- return NULL; --} -- --int ipu_prg_max_active_channels(void) --{ -- return ipu_pre_get_available_count(); --} --EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -- --bool ipu_prg_present(struct ipu_soc *ipu) --{ -- if (ipu->prg_priv) -- return true; -- -- return false; --} --EXPORT_SYMBOL_GPL(ipu_prg_present); -- --bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -- uint64_t modifier) --{ -- const struct drm_format_info *info = drm_format_info(format); -- -- if (info->num_planes != 1) -- return false; -- -- switch (modifier) { -- case DRM_FORMAT_MOD_LINEAR: -- case DRM_FORMAT_MOD_VIVANTE_TILED: -- case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: -- return true; -- default: -- return false; -- } --} --EXPORT_SYMBOL_GPL(ipu_prg_format_supported); -- --int ipu_prg_enable(struct ipu_soc *ipu) --{ -- struct ipu_prg *prg = ipu->prg_priv; -- -- if (!prg) -- return 0; -- -- return pm_runtime_get_sync(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_enable); -- --void ipu_prg_disable(struct ipu_soc *ipu) --{ -- struct ipu_prg *prg = ipu->prg_priv; -- -- if (!prg) -- return; -- -- pm_runtime_put(prg->dev); --} --EXPORT_SYMBOL_GPL(ipu_prg_disable); -- --/* -- * The channel configuartion functions below are not thread safe, as they -- * must be only called from the atomic commit path in the DRM driver, which -- * is properly serialized. -- */ --static int ipu_prg_ipu_to_prg_chan(int ipu_chan) --{ -- /* -- * This isn't clearly documented in the RM, but IPU to PRG channel -- * assignment is fixed, as only with this mapping the control signals -- * match up. -- */ -- switch (ipu_chan) { -- case IPUV3_CHANNEL_MEM_BG_SYNC: -- return 0; -- case IPUV3_CHANNEL_MEM_FG_SYNC: -- return 1; -- case IPUV3_CHANNEL_MEM_DC_SYNC: -- return 2; -- default: -- return -EINVAL; -- } --} -- --static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) --{ -- int i, ret; -- -- /* channel 0 is special as it is hardwired to one of the PREs */ -- if (prg_chan == 0) { -- ret = ipu_pre_get(prg->pres[0]); -- if (ret) -- goto fail; -- prg->chan[prg_chan].used_pre = 0; -- return 0; -- } -- -- for (i = 1; i < 3; i++) { -- ret = ipu_pre_get(prg->pres[i]); -- if (!ret) { -- u32 val, mux; -- int shift; -- -- prg->chan[prg_chan].used_pre = i; -- -- /* configure the PRE to PRG channel mux */ -- shift = (i == 1) ? 12 : 14; -- mux = (prg->id << 1) | (prg_chan - 1); -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, mux << shift); -- -- /* check other mux, must not point to same channel */ -- shift = (i == 1) ? 14 : 12; -- regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -- if (((val >> shift) & 0x3) == mux) { -- regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -- 0x3 << shift, -- (mux ^ 0x1) << shift); -- } -- -- return 0; -- } -- } -- --fail: -- dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -- return ret; --} -- --static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) --{ -- struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -- -- ipu_pre_put(prg->pres[chan->used_pre]); -- chan->used_pre = -1; --} -- --void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- u32 val; -- -- if (prg_chan < 0) -- return; -- -- chan = &prg->chan[prg_chan]; -- if (!chan->enabled) -- return; -- -- pm_runtime_get_sync(prg->dev); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- pm_runtime_put(prg->dev); -- -- ipu_prg_put_pre(prg, prg_chan); -- -- chan->enabled = false; --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); -- --int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, -- unsigned int axi_id, unsigned int width, -- unsigned int height, unsigned int stride, -- u32 format, uint64_t modifier, unsigned long *eba) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- u32 val; -- int ret; -- -- if (prg_chan < 0) -- return prg_chan; -- -- chan = &prg->chan[prg_chan]; -- -- if (chan->enabled) { -- ipu_pre_update(prg->pres[chan->used_pre], *eba); -- return 0; -- } -- -- ret = ipu_prg_get_pre(prg, prg_chan); -- if (ret) -- return ret; -- -- ipu_pre_configure(prg->pres[chan->used_pre], -- width, height, stride, format, modifier, *eba); -- -- -- pm_runtime_get_sync(prg->dev); -- -- val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -- writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -- -- val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -- ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -- IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -- writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -- -- val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -- *eba = val; -- writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -- -- val = readl(prg->regs + IPU_PRG_CTL); -- /* config AXI ID */ -- val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -- IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -- val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -- /* enable channel */ -- val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -- writel(val, prg->regs + IPU_PRG_CTL); -- -- val = IPU_PRG_REG_UPDATE_REG_UPDATE; -- writel(val, prg->regs + IPU_PRG_REG_UPDATE); -- -- /* wait for both double buffers to be filled */ -- readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -- (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -- (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -- 5, 1000); -- -- pm_runtime_put(prg->dev); -- -- chan->enabled = true; -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -- --bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) --{ -- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -- struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -- struct ipu_prg_channel *chan; -- -- if (prg_chan < 0) -- return false; -- -- chan = &prg->chan[prg_chan]; -- WARN_ON(!chan->enabled); -- -- return ipu_pre_update_pending(prg->pres[chan->used_pre]); --} --EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -- --static int ipu_prg_probe(struct platform_device *pdev) --{ -- struct device *dev = &pdev->dev; -- struct resource *res; -- struct ipu_prg *prg; -- u32 val; -- int i, ret; -- -- prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -- if (!prg) -- return -ENOMEM; -- -- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- prg->regs = devm_ioremap_resource(&pdev->dev, res); -- if (IS_ERR(prg->regs)) -- return PTR_ERR(prg->regs); -- -- -- prg->clk_ipg = devm_clk_get(dev, "ipg"); -- if (IS_ERR(prg->clk_ipg)) -- return PTR_ERR(prg->clk_ipg); -- -- prg->clk_axi = devm_clk_get(dev, "axi"); -- if (IS_ERR(prg->clk_axi)) -- return PTR_ERR(prg->clk_axi); -- -- prg->iomuxc_gpr = -- syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -- if (IS_ERR(prg->iomuxc_gpr)) -- return PTR_ERR(prg->iomuxc_gpr); -- -- for (i = 0; i < 3; i++) { -- prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -- if (!prg->pres[i]) -- return -EPROBE_DEFER; -- } -- -- ret = clk_prepare_enable(prg->clk_ipg); -- if (ret) -- return ret; -- -- ret = clk_prepare_enable(prg->clk_axi); -- if (ret) { -- clk_disable_unprepare(prg->clk_ipg); -- return ret; -- } -- -- /* init to free running mode */ -- val = readl(prg->regs + IPU_PRG_CTL); -- val |= IPU_PRG_CTL_SHADOW_EN; -- writel(val, prg->regs + IPU_PRG_CTL); -- -- /* disable address threshold */ -- writel(0xffffffff, prg->regs + IPU_PRG_THD); -- -- pm_runtime_set_active(dev); -- pm_runtime_enable(dev); -- -- prg->dev = dev; -- platform_set_drvdata(pdev, prg); -- mutex_lock(&ipu_prg_list_mutex); -- list_add(&prg->list, &ipu_prg_list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- return 0; --} -- --static int ipu_prg_remove(struct platform_device *pdev) --{ -- struct ipu_prg *prg = platform_get_drvdata(pdev); -- -- mutex_lock(&ipu_prg_list_mutex); -- list_del(&prg->list); -- mutex_unlock(&ipu_prg_list_mutex); -- -- return 0; --} -- --#ifdef CONFIG_PM --static int prg_suspend(struct device *dev) --{ -- struct ipu_prg *prg = dev_get_drvdata(dev); -- -- clk_disable_unprepare(prg->clk_axi); -- clk_disable_unprepare(prg->clk_ipg); -- -- return 0; --} -- --static int prg_resume(struct device *dev) --{ -- struct ipu_prg *prg = dev_get_drvdata(dev); -- int ret; -- -- ret = clk_prepare_enable(prg->clk_ipg); -- if (ret) -- return ret; -- -- ret = clk_prepare_enable(prg->clk_axi); -- if (ret) { -- clk_disable_unprepare(prg->clk_ipg); -- return ret; -- } -- -- return 0; --} --#endif -- --static const struct dev_pm_ops prg_pm_ops = { -- SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) --}; -- --static const struct of_device_id ipu_prg_dt_ids[] = { -- { .compatible = "fsl,imx6qp-prg", }, -- { /* sentinel */ }, --}; -- --struct platform_driver ipu_prg_drv = { -- .probe = ipu_prg_probe, -- .remove = ipu_prg_remove, -- .driver = { -- .name = "imx-ipu-prg", -- .pm = &prg_pm_ops, -- .of_match_table = ipu_prg_dt_ids, -- }, --}; ---- a/drivers/gpu/imx/ipu-v3/ipu-prv.h -+++ /dev/null -@@ -1,274 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-or-later */ --/* -- * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#ifndef __IPU_PRV_H__ --#define __IPU_PRV_H__ -- --struct ipu_soc; -- --#include <linux/types.h> --#include <linux/device.h> --#include <linux/clk.h> --#include <linux/platform_device.h> -- --#include <video/imx-ipu-v3.h> -- --#define IPU_MCU_T_DEFAULT 8 --#define IPU_CM_IDMAC_REG_OFS 0x00008000 --#define IPU_CM_IC_REG_OFS 0x00020000 --#define IPU_CM_IRT_REG_OFS 0x00028000 --#define IPU_CM_CSI0_REG_OFS 0x00030000 --#define IPU_CM_CSI1_REG_OFS 0x00038000 --#define IPU_CM_SMFC_REG_OFS 0x00050000 --#define IPU_CM_DC_REG_OFS 0x00058000 --#define IPU_CM_DMFC_REG_OFS 0x00060000 -- --/* Register addresses */ --/* IPU Common registers */ --#define IPU_CM_REG(offset) (offset) -- --#define IPU_CONF IPU_CM_REG(0) -- --#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) --#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) --#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) --#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) --#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) --#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) --#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) --#define IPU_SKIP IPU_CM_REG(0x00bc) --#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) --#define IPU_DISP_GEN IPU_CM_REG(0x00c4) --#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) --#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) --#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) --#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) --#define IPU_SNOOP IPU_CM_REG(0x00d8) --#define IPU_MEM_RST IPU_CM_REG(0x00dc) --#define IPU_PM IPU_CM_REG(0x00e0) --#define IPU_GPR IPU_CM_REG(0x00e4) --#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) --#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) --#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) --#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) --#define IPU_SRM_STAT IPU_CM_REG(0x024C) --#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) --#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) --#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) --#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) --#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -- --#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) --#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -- --/* SRM_PRI2 */ --#define DP_S_SRM_MODE_MASK (0x3 << 3) --#define DP_S_SRM_MODE_NOW (0x3 << 3) --#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -- --/* FS_PROC_FLOW1 */ --#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) --#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) --#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) --#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) --#define FS_PP_SRC_SEL_MASK (0xf << 12) --#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) --#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) --#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) --#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) --#define FS_PRP_SRC_SEL_MASK (0xf << 24) --#define FS_VDI_SRC_SEL_MASK (0x3 << 28) --#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) --#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -- --/* FS_PROC_FLOW2 */ --#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) --#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) --#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) --#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) --#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) --#define FS_PP_DEST_SEL_MASK (0xf << 12) --#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) --#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) --#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) --#define FS_PRP_DEST_SEL_MASK (0xf << 24) -- --#define IPU_DI0_COUNTER_RELEASE (1 << 24) --#define IPU_DI1_COUNTER_RELEASE (1 << 25) -- --#define IPU_IDMAC_REG(offset) (offset) -- --#define IDMAC_CONF IPU_IDMAC_REG(0x0000) --#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) --#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) --#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) --#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) --#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) --#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) --#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) --#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) --#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) --#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) --#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) --#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -- --#define IPU_NUM_IRQS (32 * 15) -- --enum ipu_modules { -- IPU_CONF_CSI0_EN = (1 << 0), -- IPU_CONF_CSI1_EN = (1 << 1), -- IPU_CONF_IC_EN = (1 << 2), -- IPU_CONF_ROT_EN = (1 << 3), -- IPU_CONF_ISP_EN = (1 << 4), -- IPU_CONF_DP_EN = (1 << 5), -- IPU_CONF_DI0_EN = (1 << 6), -- IPU_CONF_DI1_EN = (1 << 7), -- IPU_CONF_SMFC_EN = (1 << 8), -- IPU_CONF_DC_EN = (1 << 9), -- IPU_CONF_DMFC_EN = (1 << 10), -- -- IPU_CONF_VDI_EN = (1 << 12), -- -- IPU_CONF_IDMAC_DIS = (1 << 22), -- -- IPU_CONF_IC_DMFC_SEL = (1 << 25), -- IPU_CONF_IC_DMFC_SYNC = (1 << 26), -- IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -- -- IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -- IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -- IPU_CONF_IC_INPUT = (1 << 30), -- IPU_CONF_CSI_SEL = (1 << 31), --}; -- --struct ipuv3_channel { -- unsigned int num; -- struct ipu_soc *ipu; -- struct list_head list; --}; -- --struct ipu_cpmem; --struct ipu_csi; --struct ipu_dc_priv; --struct ipu_dmfc_priv; --struct ipu_di; --struct ipu_ic_priv; --struct ipu_vdi; --struct ipu_image_convert_priv; --struct ipu_smfc_priv; --struct ipu_pre; --struct ipu_prg; -- --struct ipu_devtype; -- --struct ipu_soc { -- struct device *dev; -- const struct ipu_devtype *devtype; -- enum ipuv3_type ipu_type; -- spinlock_t lock; -- struct mutex channel_lock; -- struct list_head channels; -- -- void __iomem *cm_reg; -- void __iomem *idmac_reg; -- -- int id; -- int usecount; -- -- struct clk *clk; -- -- int irq_sync; -- int irq_err; -- struct irq_domain *domain; -- -- struct ipu_cpmem *cpmem_priv; -- struct ipu_dc_priv *dc_priv; -- struct ipu_dp_priv *dp_priv; -- struct ipu_dmfc_priv *dmfc_priv; -- struct ipu_di *di_priv[2]; -- struct ipu_csi *csi_priv[2]; -- struct ipu_ic_priv *ic_priv; -- struct ipu_vdi *vdi_priv; -- struct ipu_image_convert_priv *image_convert_priv; -- struct ipu_smfc_priv *smfc_priv; -- struct ipu_prg *prg_priv; --}; -- --static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) --{ -- return readl(ipu->idmac_reg + offset); --} -- --static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -- unsigned offset) --{ -- writel(value, ipu->idmac_reg + offset); --} -- --void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -- --int ipu_module_enable(struct ipu_soc *ipu, u32 mask); --int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -- --bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -- --int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *clk_ipu); --void ipu_csi_exit(struct ipu_soc *ipu, int id); -- --int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, unsigned long tpmem_base); --void ipu_ic_exit(struct ipu_soc *ipu); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module); --void ipu_vdi_exit(struct ipu_soc *ipu); -- --int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); --void ipu_image_convert_exit(struct ipu_soc *ipu); -- --int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -- unsigned long base, u32 module, struct clk *ipu_clk); --void ipu_di_exit(struct ipu_soc *ipu, int id); -- --int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- struct clk *ipu_clk); --void ipu_dmfc_exit(struct ipu_soc *ipu); -- --int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_dp_exit(struct ipu_soc *ipu); -- --int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -- unsigned long template_base); --void ipu_dc_exit(struct ipu_soc *ipu); -- --int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_cpmem_exit(struct ipu_soc *ipu); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); --void ipu_smfc_exit(struct ipu_soc *ipu); -- --struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -- int index); --int ipu_pre_get_available_count(void); --int ipu_pre_get(struct ipu_pre *pre); --void ipu_pre_put(struct ipu_pre *pre); --u32 ipu_pre_get_baddr(struct ipu_pre *pre); --void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -- unsigned int height, unsigned int stride, u32 format, -- uint64_t modifier, unsigned int bufaddr); --void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); --bool ipu_pre_update_pending(struct ipu_pre *pre); -- --struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -- int ipu_id); -- --extern struct platform_driver ipu_pre_drv; --extern struct platform_driver ipu_prg_drv; -- --#endif /* __IPU_PRV_H__ */ ---- a/drivers/gpu/imx/ipu-v3/ipu-smfc.c -+++ /dev/null -@@ -1,202 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -- */ --#include <linux/export.h> --#include <linux/types.h> --#include <linux/init.h> --#include <linux/io.h> --#include <linux/errno.h> --#include <linux/spinlock.h> --#include <linux/delay.h> --#include <linux/clk.h> --#include <video/imx-ipu-v3.h> -- --#include "ipu-prv.h" -- --struct ipu_smfc { -- struct ipu_smfc_priv *priv; -- int chno; -- bool inuse; --}; -- --struct ipu_smfc_priv { -- void __iomem *base; -- spinlock_t lock; -- struct ipu_soc *ipu; -- struct ipu_smfc channel[4]; -- int use_count; --}; -- --/*SMFC Registers */ --#define SMFC_MAP 0x0000 --#define SMFC_WMC 0x0004 --#define SMFC_BS 0x0008 -- --int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 4; -- val = readl(priv->base + SMFC_BS); -- val &= ~(0xf << shift); -- val |= burstsize << shift; -- writel(val, priv->base + SMFC_BS); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -- --int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 3; -- val = readl(priv->base + SMFC_MAP); -- val &= ~(0x7 << shift); -- val |= ((csi_id << 2) | mipi_id) << shift; -- writel(val, priv->base + SMFC_MAP); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -- --int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- u32 val, shift; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -- val = readl(priv->base + SMFC_WMC); -- val &= ~(0x3f << shift); -- val |= ((clr_level << 3) | set_level) << shift; -- writel(val, priv->base + SMFC_WMC); -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -- --int ipu_smfc_enable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (!priv->use_count) -- ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -- -- priv->use_count++; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_enable); -- --int ipu_smfc_disable(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- priv->use_count--; -- -- if (!priv->use_count) -- ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -- -- if (priv->use_count < 0) -- priv->use_count = 0; -- -- spin_unlock_irqrestore(&priv->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_smfc_disable); -- --struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) --{ -- struct ipu_smfc_priv *priv = ipu->smfc_priv; -- struct ipu_smfc *smfc, *ret; -- unsigned long flags; -- -- if (chno >= 4) -- return ERR_PTR(-EINVAL); -- -- smfc = &priv->channel[chno]; -- ret = smfc; -- -- spin_lock_irqsave(&priv->lock, flags); -- -- if (smfc->inuse) { -- ret = ERR_PTR(-EBUSY); -- goto unlock; -- } -- -- smfc->inuse = true; --unlock: -- spin_unlock_irqrestore(&priv->lock, flags); -- return ret; --} --EXPORT_SYMBOL_GPL(ipu_smfc_get); -- --void ipu_smfc_put(struct ipu_smfc *smfc) --{ -- struct ipu_smfc_priv *priv = smfc->priv; -- unsigned long flags; -- -- spin_lock_irqsave(&priv->lock, flags); -- smfc->inuse = false; -- spin_unlock_irqrestore(&priv->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_smfc_put); -- --int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base) --{ -- struct ipu_smfc_priv *priv; -- int i; -- -- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -- if (!priv) -- return -ENOMEM; -- -- ipu->smfc_priv = priv; -- spin_lock_init(&priv->lock); -- priv->ipu = ipu; -- -- priv->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!priv->base) -- return -ENOMEM; -- -- for (i = 0; i < 4; i++) { -- priv->channel[i].priv = priv; -- priv->channel[i].chno = i; -- } -- -- pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -- -- return 0; --} -- --void ipu_smfc_exit(struct ipu_soc *ipu) --{ --} ---- a/drivers/gpu/imx/ipu-v3/ipu-vdi.c -+++ /dev/null -@@ -1,234 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-or-later --/* -- * Copyright (C) 2012-2016 Mentor Graphics Inc. -- * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -- */ --#include <linux/io.h> --#include "ipu-prv.h" -- --struct ipu_vdi { -- void __iomem *base; -- u32 module; -- spinlock_t lock; -- int use_count; -- struct ipu_soc *ipu; --}; -- -- --/* VDI Register Offsets */ --#define VDI_FSIZE 0x0000 --#define VDI_C 0x0004 -- --/* VDI Register Fields */ --#define VDI_C_CH_420 (0 << 1) --#define VDI_C_CH_422 (1 << 1) --#define VDI_C_MOT_SEL_MASK (0x3 << 2) --#define VDI_C_MOT_SEL_FULL (2 << 2) --#define VDI_C_MOT_SEL_LOW (1 << 2) --#define VDI_C_MOT_SEL_MED (0 << 2) --#define VDI_C_BURST_SIZE1_4 (3 << 4) --#define VDI_C_BURST_SIZE2_4 (3 << 8) --#define VDI_C_BURST_SIZE3_4 (3 << 12) --#define VDI_C_BURST_SIZE_MASK 0xF --#define VDI_C_BURST_SIZE1_OFFSET 4 --#define VDI_C_BURST_SIZE2_OFFSET 8 --#define VDI_C_BURST_SIZE3_OFFSET 12 --#define VDI_C_VWM1_SET_1 (0 << 16) --#define VDI_C_VWM1_SET_2 (1 << 16) --#define VDI_C_VWM1_CLR_2 (1 << 19) --#define VDI_C_VWM3_SET_1 (0 << 22) --#define VDI_C_VWM3_SET_2 (1 << 22) --#define VDI_C_VWM3_CLR_2 (1 << 25) --#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) --#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -- --static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) --{ -- return readl(vdi->base + offset); --} -- --static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -- unsigned int offset) --{ -- writel(value, vdi->base + offset); --} -- --void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) --{ -- bool top_field_0 = false; -- unsigned long flags; -- u32 reg; -- -- switch (field) { -- case V4L2_FIELD_INTERLACED_TB: -- case V4L2_FIELD_SEQ_TB: -- case V4L2_FIELD_TOP: -- top_field_0 = true; -- break; -- case V4L2_FIELD_INTERLACED_BT: -- case V4L2_FIELD_SEQ_BT: -- case V4L2_FIELD_BOTTOM: -- top_field_0 = false; -- break; -- default: -- top_field_0 = (std & V4L2_STD_525_60) ? true : false; -- break; -- } -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- if (top_field_0) -- reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -- else -- reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -- --void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) --{ -- unsigned long flags; -- u32 reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ipu_vdi_read(vdi, VDI_C); -- -- reg &= ~VDI_C_MOT_SEL_MASK; -- -- switch (motion_sel) { -- case MED_MOTION: -- reg |= VDI_C_MOT_SEL_MED; -- break; -- case HIGH_MOTION: -- reg |= VDI_C_MOT_SEL_FULL; -- break; -- default: -- reg |= VDI_C_MOT_SEL_LOW; -- break; -- } -- -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -- --void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) --{ -- unsigned long flags; -- u32 pixel_fmt, reg; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- reg = ((yres - 1) << 16) | (xres - 1); -- ipu_vdi_write(vdi, reg, VDI_FSIZE); -- -- /* -- * Full motion, only vertical filter is used. -- * Burst size is 4 accesses -- */ -- if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -- code == MEDIA_BUS_FMT_UYVY8_1X16 || -- code == MEDIA_BUS_FMT_YUYV8_2X8 || -- code == MEDIA_BUS_FMT_YUYV8_1X16) -- pixel_fmt = VDI_C_CH_422; -- else -- pixel_fmt = VDI_C_CH_420; -- -- reg = ipu_vdi_read(vdi, VDI_C); -- reg |= pixel_fmt; -- reg |= VDI_C_BURST_SIZE2_4; -- reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -- reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -- ipu_vdi_write(vdi, reg, VDI_C); -- -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_setup); -- --void ipu_vdi_unsetup(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- ipu_vdi_write(vdi, 0, VDI_FSIZE); -- ipu_vdi_write(vdi, 0, VDI_C); -- spin_unlock_irqrestore(&vdi->lock, flags); --} --EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -- --int ipu_vdi_enable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (!vdi->use_count) -- ipu_module_enable(vdi->ipu, vdi->module); -- -- vdi->use_count++; -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_enable); -- --int ipu_vdi_disable(struct ipu_vdi *vdi) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&vdi->lock, flags); -- -- if (vdi->use_count) { -- if (!--vdi->use_count) -- ipu_module_disable(vdi->ipu, vdi->module); -- } -- -- spin_unlock_irqrestore(&vdi->lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_GPL(ipu_vdi_disable); -- --struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) --{ -- return ipu->vdi_priv; --} --EXPORT_SYMBOL_GPL(ipu_vdi_get); -- --void ipu_vdi_put(struct ipu_vdi *vdi) --{ --} --EXPORT_SYMBOL_GPL(ipu_vdi_put); -- --int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -- unsigned long base, u32 module) --{ -- struct ipu_vdi *vdi; -- -- vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -- if (!vdi) -- return -ENOMEM; -- -- ipu->vdi_priv = vdi; -- -- spin_lock_init(&vdi->lock); -- vdi->module = module; -- vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -- if (!vdi->base) -- return -ENOMEM; -- -- dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -- vdi->ipu = ipu; -- -- return 0; --} -- --void ipu_vdi_exit(struct ipu_soc *ipu) --{ --} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/Kconfig -@@ -0,0 +1,11 @@ -+# SPDX-License-Identifier: GPL-2.0-only -+config IMX_IPUV3_CORE -+ tristate "IPUv3 core support" -+ depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST -+ depends on DRM || !DRM # if DRM=m, this can't be 'y' -+ select BITREVERSE -+ select GENERIC_ALLOCATOR if DRM -+ select GENERIC_IRQ_CHIP -+ help -+ Choose this if you have a i.MX5/6 system and want to use the Image -+ Processing Unit. This option only enables IPU base support. ---- /dev/null -+++ b/drivers/gpu/ipu-v3/Makefile -@@ -0,0 +1,10 @@ -+# SPDX-License-Identifier: GPL-2.0 -+obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o -+ -+imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ -+ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-ic-csc.o \ -+ ipu-image-convert.o ipu-smfc.o ipu-vdi.o -+ -+ifdef CONFIG_DRM -+ imx-ipu-v3-objs += ipu-pre.o ipu-prg.o -+endif ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-common.c -@@ -0,0 +1,1565 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/module.h> -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/reset.h> -+#include <linux/platform_device.h> -+#include <linux/err.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/clk.h> -+#include <linux/list.h> -+#include <linux/irq.h> -+#include <linux/irqchip/chained_irq.h> -+#include <linux/irqdomain.h> -+#include <linux/of_device.h> -+#include <linux/of_graph.h> -+ -+#include <drm/drm_fourcc.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->cm_reg + offset); -+} -+ -+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset) -+{ -+ writel(value, ipu->cm_reg + offset); -+} -+ -+int ipu_get_num(struct ipu_soc *ipu) -+{ -+ return ipu->id; -+} -+EXPORT_SYMBOL_GPL(ipu_get_num); -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync) -+{ -+ u32 val; -+ -+ val = ipu_cm_read(ipu, IPU_SRM_PRI2); -+ val &= ~DP_S_SRM_MODE_MASK; -+ val |= sync ? DP_S_SRM_MODE_NEXT_FRAME : -+ DP_S_SRM_MODE_NOW; -+ ipu_cm_write(ipu, val, IPU_SRM_PRI2); -+} -+EXPORT_SYMBOL_GPL(ipu_srm_dp_update); -+ -+enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_ARGB1555: -+ case DRM_FORMAT_ABGR1555: -+ case DRM_FORMAT_RGBA5551: -+ case DRM_FORMAT_BGRA5551: -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_ARGB4444: -+ case DRM_FORMAT_XRGB8888: -+ case DRM_FORMAT_XBGR8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ return IPUV3_COLORSPACE_RGB; -+ case DRM_FORMAT_YUYV: -+ case DRM_FORMAT_UYVY: -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ case DRM_FORMAT_NV12: -+ case DRM_FORMAT_NV21: -+ case DRM_FORMAT_NV16: -+ case DRM_FORMAT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_drm_fourcc_to_colorspace); -+ -+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return IPUV3_COLORSPACE_YUV; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ return IPUV3_COLORSPACE_RGB; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace); -+ -+bool ipu_pixelformat_is_planar(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ return true; -+ } -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_pixelformat_is_planar); -+ -+enum ipu_color_space ipu_mbus_code_to_colorspace(u32 mbus_code) -+{ -+ switch (mbus_code & 0xf000) { -+ case 0x1000: -+ return IPUV3_COLORSPACE_RGB; -+ case 0x2000: -+ return IPUV3_COLORSPACE_YUV; -+ default: -+ return IPUV3_COLORSPACE_UNKNOWN; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_mbus_code_to_colorspace); -+ -+int ipu_stride_to_bytes(u32 pixel_stride, u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ case V4L2_PIX_FMT_YUV422P: -+ case V4L2_PIX_FMT_NV12: -+ case V4L2_PIX_FMT_NV21: -+ case V4L2_PIX_FMT_NV16: -+ case V4L2_PIX_FMT_NV61: -+ /* -+ * for the planar YUV formats, the stride passed to -+ * cpmem must be the stride in bytes of the Y plane. -+ * And all the planar YUV formats have an 8-bit -+ * Y component. -+ */ -+ return (8 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_RGB565: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_UYVY: -+ return (16 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR24: -+ case V4L2_PIX_FMT_RGB24: -+ return (24 * pixel_stride) >> 3; -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_XRGB32: -+ return (32 * pixel_stride) >> 3; -+ default: -+ break; -+ } -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL_GPL(ipu_stride_to_bytes); -+ -+int ipu_degrees_to_rot_mode(enum ipu_rotate_mode *mode, int degrees, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ switch (degrees) { -+ case 0: -+ vf = hf = r90 = 0; -+ break; -+ case 90: -+ vf = hf = 0; -+ r90 = 1; -+ break; -+ case 180: -+ vf = hf = 1; -+ r90 = 0; -+ break; -+ case 270: -+ vf = hf = r90 = 1; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ *mode = (enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_degrees_to_rot_mode); -+ -+int ipu_rot_mode_to_degrees(int *degrees, enum ipu_rotate_mode mode, -+ bool hflip, bool vflip) -+{ -+ u32 r90, vf, hf; -+ -+ r90 = ((u32)mode >> 2) & 0x1; -+ hf = ((u32)mode >> 1) & 0x1; -+ vf = ((u32)mode >> 0) & 0x1; -+ hf ^= (u32)hflip; -+ vf ^= (u32)vflip; -+ -+ switch ((enum ipu_rotate_mode)((r90 << 2) | (hf << 1) | vf)) { -+ case IPU_ROTATE_NONE: -+ *degrees = 0; -+ break; -+ case IPU_ROTATE_90_RIGHT: -+ *degrees = 90; -+ break; -+ case IPU_ROTATE_180: -+ *degrees = 180; -+ break; -+ case IPU_ROTATE_90_LEFT: -+ *degrees = 270; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_rot_mode_to_degrees); -+ -+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num) -+{ -+ struct ipuv3_channel *channel; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, num); -+ -+ if (num > 63) -+ return ERR_PTR(-ENODEV); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_for_each_entry(channel, &ipu->channels, list) { -+ if (channel->num == num) { -+ channel = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ } -+ -+ channel = kzalloc(sizeof(*channel), GFP_KERNEL); -+ if (!channel) { -+ channel = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ channel->num = num; -+ channel->ipu = ipu; -+ list_add(&channel->list, &ipu->channels); -+ -+out: -+ mutex_unlock(&ipu->channel_lock); -+ -+ return channel; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get); -+ -+void ipu_idmac_put(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ -+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num); -+ -+ mutex_lock(&ipu->channel_lock); -+ -+ list_del(&channel->list); -+ kfree(channel); -+ -+ mutex_unlock(&ipu->channel_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_put); -+ -+#define idma_mask(ch) (1 << ((ch) & 0x1f)) -+ -+/* -+ * This is an undocumented feature, a write one to a channel bit in -+ * IPU_CHA_CUR_BUF and IPU_CHA_TRIPLE_CUR_BUF will reset the channel's -+ * internal current buffer pointer so that transfers start from buffer -+ * 0 on the next channel enable (that's the theory anyway, the imx6 TRM -+ * only says these are read-only registers). This operation is required -+ * for channel linking to work correctly, for instance video capture -+ * pipelines that carry out image rotations will fail after the first -+ * streaming unless this function is called for each channel before -+ * re-enabling the channels. -+ */ -+static void __ipu_idmac_reset_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_CUR_BUF(chno)); -+} -+ -+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel, -+ bool doublebuffer) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ if (doublebuffer) -+ reg |= idma_mask(channel->num); -+ else -+ reg &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer); -+ -+static const struct { -+ int chnum; -+ u32 reg; -+ int shift; -+} idmac_lock_en_info[] = { -+ { .chnum = 5, .reg = IDMAC_CH_LOCK_EN_1, .shift = 0, }, -+ { .chnum = 11, .reg = IDMAC_CH_LOCK_EN_1, .shift = 2, }, -+ { .chnum = 12, .reg = IDMAC_CH_LOCK_EN_1, .shift = 4, }, -+ { .chnum = 14, .reg = IDMAC_CH_LOCK_EN_1, .shift = 6, }, -+ { .chnum = 15, .reg = IDMAC_CH_LOCK_EN_1, .shift = 8, }, -+ { .chnum = 20, .reg = IDMAC_CH_LOCK_EN_1, .shift = 10, }, -+ { .chnum = 21, .reg = IDMAC_CH_LOCK_EN_1, .shift = 12, }, -+ { .chnum = 22, .reg = IDMAC_CH_LOCK_EN_1, .shift = 14, }, -+ { .chnum = 23, .reg = IDMAC_CH_LOCK_EN_1, .shift = 16, }, -+ { .chnum = 27, .reg = IDMAC_CH_LOCK_EN_1, .shift = 18, }, -+ { .chnum = 28, .reg = IDMAC_CH_LOCK_EN_1, .shift = 20, }, -+ { .chnum = 45, .reg = IDMAC_CH_LOCK_EN_2, .shift = 0, }, -+ { .chnum = 46, .reg = IDMAC_CH_LOCK_EN_2, .shift = 2, }, -+ { .chnum = 47, .reg = IDMAC_CH_LOCK_EN_2, .shift = 4, }, -+ { .chnum = 48, .reg = IDMAC_CH_LOCK_EN_2, .shift = 6, }, -+ { .chnum = 49, .reg = IDMAC_CH_LOCK_EN_2, .shift = 8, }, -+ { .chnum = 50, .reg = IDMAC_CH_LOCK_EN_2, .shift = 10, }, -+}; -+ -+int ipu_idmac_lock_enable(struct ipuv3_channel *channel, int num_bursts) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 bursts, regval; -+ int i; -+ -+ switch (num_bursts) { -+ case 0: -+ case 1: -+ bursts = 0x00; /* locking disabled */ -+ break; -+ case 2: -+ bursts = 0x01; -+ break; -+ case 4: -+ bursts = 0x02; -+ break; -+ case 8: -+ bursts = 0x03; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* -+ * IPUv3EX / i.MX51 has a different register layout, and on IPUv3M / -+ * i.MX53 channel arbitration locking doesn't seem to work properly. -+ * Allow enabling the lock feature on IPUv3H / i.MX6 only. -+ */ -+ if (bursts && ipu->ipu_type != IPUV3H) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(idmac_lock_en_info); i++) { -+ if (channel->num == idmac_lock_en_info[i].chnum) -+ break; -+ } -+ if (i >= ARRAY_SIZE(idmac_lock_en_info)) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ regval = ipu_idmac_read(ipu, idmac_lock_en_info[i].reg); -+ regval &= ~(0x03 << idmac_lock_en_info[i].shift); -+ regval |= (bursts << idmac_lock_en_info[i].shift); -+ ipu_idmac_write(ipu, regval, idmac_lock_en_info[i].reg); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_lock_enable); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val |= IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val |= IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val |= mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_enable); -+ -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask) -+{ -+ unsigned long lock_flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, lock_flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ val = ipu_cm_read(ipu, IPU_DISP_GEN); -+ -+ if (mask & IPU_CONF_DI0_EN) -+ val &= ~IPU_DI0_COUNTER_RELEASE; -+ if (mask & IPU_CONF_DI1_EN) -+ val &= ~IPU_DI1_COUNTER_RELEASE; -+ -+ ipu_cm_write(ipu, val, IPU_DISP_GEN); -+ -+ spin_unlock_irqrestore(&ipu->lock, lock_flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_module_disable); -+ -+int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ -+ return (ipu_cm_read(ipu, IPU_CHA_CUR_BUF(chno)) & idma_mask(chno)) ? 1 : 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_get_current_buffer); -+ -+bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 reg = 0; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ switch (buf_num) { -+ case 0: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)); -+ break; -+ case 1: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)); -+ break; -+ case 2: -+ reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(channel->num)); -+ break; -+ } -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return ((reg & idma_mask(channel->num)) != 0); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_buffer_is_ready); -+ -+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Mark buffer as ready. */ -+ if (buf_num == 0) -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ else -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer); -+ -+void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned int chno = channel->num; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ -+ switch (buf_num) { -+ case 0: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno)); -+ break; -+ case 1: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno)); -+ break; -+ case 2: -+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF2_RDY(chno)); -+ break; -+ default: -+ break; -+ } -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_clear_buffer); -+ -+int ipu_idmac_enable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val |= idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno) -+{ -+ return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno)); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy); -+ -+int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long timeout; -+ -+ timeout = jiffies + msecs_to_jiffies(ms); -+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) & -+ idma_mask(channel->num)) { -+ if (time_after(jiffies, timeout)) -+ return -ETIMEDOUT; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy); -+ -+int ipu_idmac_disable_channel(struct ipuv3_channel *channel) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ u32 val; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ /* Disable DMA channel(s) */ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num)); -+ -+ __ipu_idmac_reset_current_buffer(channel); -+ -+ /* Set channel buffers NOT to be ready */ -+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */ -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF0_RDY(channel->num)); -+ } -+ -+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) & -+ idma_mask(channel->num)) { -+ ipu_cm_write(ipu, idma_mask(channel->num), -+ IPU_CHA_BUF1_RDY(channel->num)); -+ } -+ -+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ -+ -+ /* Reset the double buffer */ -+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num)); -+ val &= ~idma_mask(channel->num); -+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel); -+ -+/* -+ * The imx6 rev. D TRM says that enabling the WM feature will increase -+ * a channel's priority. Refer to Table 36-8 Calculated priority value. -+ * The sub-module that is the sink or source for the channel must enable -+ * watermark signal for this to take effect (SMFC_WM for instance). -+ */ -+void ipu_idmac_enable_watermark(struct ipuv3_channel *channel, bool enable) -+{ -+ struct ipu_soc *ipu = channel->ipu; -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_idmac_read(ipu, IDMAC_WM_EN(channel->num)); -+ if (enable) -+ val |= 1 << (channel->num % 32); -+ else -+ val &= ~(1 << (channel->num % 32)); -+ ipu_idmac_write(ipu, val, IDMAC_WM_EN(channel->num)); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_enable_watermark); -+ -+static int ipu_memory_reset(struct ipu_soc *ipu) -+{ -+ unsigned long timeout; -+ -+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); -+ -+ timeout = jiffies + msecs_to_jiffies(1000); -+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { -+ if (time_after(jiffies, timeout)) -+ return -ETIME; -+ cpu_relax(); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Set the source mux for the given CSI. Selects either parallel or -+ * MIPI CSI2 sources. -+ */ -+void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2) -+{ -+ unsigned long flags; -+ u32 val, mask; -+ -+ mask = (csi_id == 1) ? IPU_CONF_CSI1_DATA_SOURCE : -+ IPU_CONF_CSI0_DATA_SOURCE; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (mipi_csi2) -+ val |= mask; -+ else -+ val &= ~mask; -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_csi_src_mux); -+ -+/* -+ * Set the source mux for the IC. Selects either CSI[01] or the VDI. -+ */ -+void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi) -+{ -+ unsigned long flags; -+ u32 val; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ val = ipu_cm_read(ipu, IPU_CONF); -+ if (vdi) -+ val |= IPU_CONF_IC_INPUT; -+ else -+ val &= ~IPU_CONF_IC_INPUT; -+ -+ if (csi_id == 1) -+ val |= IPU_CONF_CSI_SEL; -+ else -+ val &= ~IPU_CONF_CSI_SEL; -+ -+ ipu_cm_write(ipu, val, IPU_CONF); -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux); -+ -+ -+/* Frame Synchronization Unit Channel Linking */ -+ -+struct fsu_link_reg_info { -+ int chno; -+ u32 reg; -+ u32 mask; -+ u32 val; -+}; -+ -+struct fsu_link_info { -+ struct fsu_link_reg_info src; -+ struct fsu_link_reg_info sink; -+}; -+ -+static const struct fsu_link_info fsu_link_info[] = { -+ { -+ .src = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1, -+ FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2, -+ FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1, -+ FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF }, -+ }, { -+ .src = { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2, -+ FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP }, -+ .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1, -+ FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP }, -+ }, { -+ .src = { IPUV3_CHANNEL_CSI_DIRECT, 0 }, -+ .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1, -+ FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT }, -+ }, -+}; -+ -+static const struct fsu_link_info *find_fsu_link_info(int src, int sink) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) { -+ if (src == fsu_link_info[i].src.chno && -+ sink == fsu_link_info[i].sink.chno) -+ return &fsu_link_info[i]; -+ } -+ -+ return NULL; -+} -+ -+/* -+ * Links a source channel to a sink channel in the FSU. -+ */ -+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ src_reg |= link->src.val; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ sink_reg |= link->sink.val; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_link); -+ -+/* -+ * Unlinks source and sink channels in the FSU. -+ */ -+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch) -+{ -+ const struct fsu_link_info *link; -+ u32 src_reg, sink_reg; -+ unsigned long flags; -+ -+ link = find_fsu_link_info(src_ch, sink_ch); -+ if (!link) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&ipu->lock, flags); -+ -+ if (link->src.mask) { -+ src_reg = ipu_cm_read(ipu, link->src.reg); -+ src_reg &= ~link->src.mask; -+ ipu_cm_write(ipu, src_reg, link->src.reg); -+ } -+ -+ if (link->sink.mask) { -+ sink_reg = ipu_cm_read(ipu, link->sink.reg); -+ sink_reg &= ~link->sink.mask; -+ ipu_cm_write(ipu, sink_reg, link->sink.reg); -+ } -+ -+ spin_unlock_irqrestore(&ipu->lock, flags); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_fsu_unlink); -+ -+/* Link IDMAC channels in the FSU */ -+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_link(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_link); -+ -+/* Unlink IDMAC channels in the FSU */ -+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink) -+{ -+ return ipu_fsu_unlink(src->ipu, src->num, sink->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_unlink); -+ -+struct ipu_devtype { -+ const char *name; -+ unsigned long cm_ofs; -+ unsigned long cpmem_ofs; -+ unsigned long srm_ofs; -+ unsigned long tpm_ofs; -+ unsigned long csi0_ofs; -+ unsigned long csi1_ofs; -+ unsigned long ic_ofs; -+ unsigned long disp0_ofs; -+ unsigned long disp1_ofs; -+ unsigned long dc_tmpl_ofs; -+ unsigned long vdi_ofs; -+ enum ipuv3_type type; -+}; -+ -+static struct ipu_devtype ipu_type_imx51 = { -+ .name = "IPUv3EX", -+ .cm_ofs = 0x1e000000, -+ .cpmem_ofs = 0x1f000000, -+ .srm_ofs = 0x1f040000, -+ .tpm_ofs = 0x1f060000, -+ .csi0_ofs = 0x1e030000, -+ .csi1_ofs = 0x1e038000, -+ .ic_ofs = 0x1e020000, -+ .disp0_ofs = 0x1e040000, -+ .disp1_ofs = 0x1e048000, -+ .dc_tmpl_ofs = 0x1f080000, -+ .vdi_ofs = 0x1e068000, -+ .type = IPUV3EX, -+}; -+ -+static struct ipu_devtype ipu_type_imx53 = { -+ .name = "IPUv3M", -+ .cm_ofs = 0x06000000, -+ .cpmem_ofs = 0x07000000, -+ .srm_ofs = 0x07040000, -+ .tpm_ofs = 0x07060000, -+ .csi0_ofs = 0x06030000, -+ .csi1_ofs = 0x06038000, -+ .ic_ofs = 0x06020000, -+ .disp0_ofs = 0x06040000, -+ .disp1_ofs = 0x06048000, -+ .dc_tmpl_ofs = 0x07080000, -+ .vdi_ofs = 0x06068000, -+ .type = IPUV3M, -+}; -+ -+static struct ipu_devtype ipu_type_imx6q = { -+ .name = "IPUv3H", -+ .cm_ofs = 0x00200000, -+ .cpmem_ofs = 0x00300000, -+ .srm_ofs = 0x00340000, -+ .tpm_ofs = 0x00360000, -+ .csi0_ofs = 0x00230000, -+ .csi1_ofs = 0x00238000, -+ .ic_ofs = 0x00220000, -+ .disp0_ofs = 0x00240000, -+ .disp1_ofs = 0x00248000, -+ .dc_tmpl_ofs = 0x00380000, -+ .vdi_ofs = 0x00268000, -+ .type = IPUV3H, -+}; -+ -+static const struct of_device_id imx_ipu_dt_ids[] = { -+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, -+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, -+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, -+ { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, -+ { /* sentinel */ } -+}; -+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); -+ -+static int ipu_submodules_init(struct ipu_soc *ipu, -+ struct platform_device *pdev, unsigned long ipu_base, -+ struct clk *ipu_clk) -+{ -+ char *unit; -+ int ret; -+ struct device *dev = &pdev->dev; -+ const struct ipu_devtype *devtype = ipu->devtype; -+ -+ ret = ipu_cpmem_init(ipu, dev, ipu_base + devtype->cpmem_ofs); -+ if (ret) { -+ unit = "cpmem"; -+ goto err_cpmem; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 0, ipu_base + devtype->csi0_ofs, -+ IPU_CONF_CSI0_EN, ipu_clk); -+ if (ret) { -+ unit = "csi0"; -+ goto err_csi_0; -+ } -+ -+ ret = ipu_csi_init(ipu, dev, 1, ipu_base + devtype->csi1_ofs, -+ IPU_CONF_CSI1_EN, ipu_clk); -+ if (ret) { -+ unit = "csi1"; -+ goto err_csi_1; -+ } -+ -+ ret = ipu_ic_init(ipu, dev, -+ ipu_base + devtype->ic_ofs, -+ ipu_base + devtype->tpm_ofs); -+ if (ret) { -+ unit = "ic"; -+ goto err_ic; -+ } -+ -+ ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs, -+ IPU_CONF_VDI_EN | IPU_CONF_ISP_EN | -+ IPU_CONF_IC_INPUT); -+ if (ret) { -+ unit = "vdi"; -+ goto err_vdi; -+ } -+ -+ ret = ipu_image_convert_init(ipu, dev); -+ if (ret) { -+ unit = "image_convert"; -+ goto err_image_convert; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs, -+ IPU_CONF_DI0_EN, ipu_clk); -+ if (ret) { -+ unit = "di0"; -+ goto err_di_0; -+ } -+ -+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs, -+ IPU_CONF_DI1_EN, ipu_clk); -+ if (ret) { -+ unit = "di1"; -+ goto err_di_1; -+ } -+ -+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs + -+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs); -+ if (ret) { -+ unit = "dc_template"; -+ goto err_dc; -+ } -+ -+ ret = ipu_dmfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk); -+ if (ret) { -+ unit = "dmfc"; -+ goto err_dmfc; -+ } -+ -+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs); -+ if (ret) { -+ unit = "dp"; -+ goto err_dp; -+ } -+ -+ ret = ipu_smfc_init(ipu, dev, ipu_base + -+ devtype->cm_ofs + IPU_CM_SMFC_REG_OFS); -+ if (ret) { -+ unit = "smfc"; -+ goto err_smfc; -+ } -+ -+ return 0; -+ -+err_smfc: -+ ipu_dp_exit(ipu); -+err_dp: -+ ipu_dmfc_exit(ipu); -+err_dmfc: -+ ipu_dc_exit(ipu); -+err_dc: -+ ipu_di_exit(ipu, 1); -+err_di_1: -+ ipu_di_exit(ipu, 0); -+err_di_0: -+ ipu_image_convert_exit(ipu); -+err_image_convert: -+ ipu_vdi_exit(ipu); -+err_vdi: -+ ipu_ic_exit(ipu); -+err_ic: -+ ipu_csi_exit(ipu, 1); -+err_csi_1: -+ ipu_csi_exit(ipu, 0); -+err_csi_0: -+ ipu_cpmem_exit(ipu); -+err_cpmem: -+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret); -+ return ret; -+} -+ -+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs) -+{ -+ unsigned long status; -+ int i, bit, irq; -+ -+ for (i = 0; i < num_regs; i++) { -+ -+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i])); -+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i])); -+ -+ for_each_set_bit(bit, &status, 32) { -+ irq = irq_linear_revmap(ipu->domain, -+ regs[i] * 32 + bit); -+ if (irq) -+ generic_handle_irq(irq); -+ } -+ } -+} -+ -+static void ipu_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+static void ipu_err_irq_handler(struct irq_desc *desc) -+{ -+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc); -+ struct irq_chip *chip = irq_desc_get_chip(desc); -+ static const int int_reg[] = { 4, 5, 8, 9}; -+ -+ chained_irq_enter(chip, desc); -+ -+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg)); -+ -+ chained_irq_exit(chip, desc); -+} -+ -+int ipu_map_irq(struct ipu_soc *ipu, int irq) -+{ -+ int virq; -+ -+ virq = irq_linear_revmap(ipu->domain, irq); -+ if (!virq) -+ virq = irq_create_mapping(ipu->domain, irq); -+ -+ return virq; -+} -+EXPORT_SYMBOL_GPL(ipu_map_irq); -+ -+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel, -+ enum ipu_channel_irq irq_type) -+{ -+ return ipu_map_irq(ipu, irq_type + channel->num); -+} -+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq); -+ -+static void ipu_submodules_exit(struct ipu_soc *ipu) -+{ -+ ipu_smfc_exit(ipu); -+ ipu_dp_exit(ipu); -+ ipu_dmfc_exit(ipu); -+ ipu_dc_exit(ipu); -+ ipu_di_exit(ipu, 1); -+ ipu_di_exit(ipu, 0); -+ ipu_image_convert_exit(ipu); -+ ipu_vdi_exit(ipu); -+ ipu_ic_exit(ipu); -+ ipu_csi_exit(ipu, 1); -+ ipu_csi_exit(ipu, 0); -+ ipu_cpmem_exit(ipu); -+} -+ -+static int platform_remove_devices_fn(struct device *dev, void *unused) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ -+ platform_device_unregister(pdev); -+ -+ return 0; -+} -+ -+static void platform_device_unregister_children(struct platform_device *pdev) -+{ -+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn); -+} -+ -+struct ipu_platform_reg { -+ struct ipu_client_platformdata pdata; -+ const char *name; -+}; -+ -+/* These must be in the order of the corresponding device tree port nodes */ -+static struct ipu_platform_reg client_reg[] = { -+ { -+ .pdata = { -+ .csi = 0, -+ .dma[0] = IPUV3_CHANNEL_CSI0, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .csi = 1, -+ .dma[0] = IPUV3_CHANNEL_CSI1, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-csi", -+ }, { -+ .pdata = { -+ .di = 0, -+ .dc = 5, -+ .dp = IPU_DP_FLOW_SYNC_BG, -+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .dma[1] = IPUV3_CHANNEL_MEM_FG_SYNC, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, { -+ .pdata = { -+ .di = 1, -+ .dc = 1, -+ .dp = -EINVAL, -+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .dma[1] = -EINVAL, -+ }, -+ .name = "imx-ipuv3-crtc", -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu_client_id_mutex); -+static int ipu_client_id; -+ -+static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) -+{ -+ struct device *dev = ipu->dev; -+ unsigned i; -+ int id, ret; -+ -+ mutex_lock(&ipu_client_id_mutex); -+ id = ipu_client_id; -+ ipu_client_id += ARRAY_SIZE(client_reg); -+ mutex_unlock(&ipu_client_id_mutex); -+ -+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) { -+ struct ipu_platform_reg *reg = &client_reg[i]; -+ struct platform_device *pdev; -+ struct device_node *of_node; -+ -+ /* Associate subdevice with the corresponding port node */ -+ of_node = of_graph_get_port_by_id(dev->of_node, i); -+ if (!of_node) { -+ dev_info(dev, -+ "no port@%d node in %pOF, not using %s%d\n", -+ i, dev->of_node, -+ (i / 2) ? "DI" : "CSI", i % 2); -+ continue; -+ } -+ -+ pdev = platform_device_alloc(reg->name, id++); -+ if (!pdev) { -+ ret = -ENOMEM; -+ goto err_register; -+ } -+ -+ pdev->dev.parent = dev; -+ -+ reg->pdata.of_node = of_node; -+ ret = platform_device_add_data(pdev, ®->pdata, -+ sizeof(reg->pdata)); -+ if (!ret) -+ ret = platform_device_add(pdev); -+ if (ret) { -+ platform_device_put(pdev); -+ goto err_register; -+ } -+ } -+ -+ return 0; -+ -+err_register: -+ platform_device_unregister_children(to_platform_device(dev)); -+ -+ return ret; -+} -+ -+ -+static int ipu_irq_init(struct ipu_soc *ipu) -+{ -+ struct irq_chip_generic *gc; -+ struct irq_chip_type *ct; -+ unsigned long unused[IPU_NUM_IRQS / 32] = { -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x400100d0, 0xffe000fd, -+ 0x4077ffff, 0xffe7e1fd, -+ 0x23fffffe, 0x8880fff0, -+ 0xf98fe7d0, 0xfff81fff, -+ 0x400100d0, 0xffe000fd, -+ 0x00000000, -+ }; -+ int ret, i; -+ -+ ipu->domain = irq_domain_add_linear(ipu->dev->of_node, IPU_NUM_IRQS, -+ &irq_generic_chip_ops, ipu); -+ if (!ipu->domain) { -+ dev_err(ipu->dev, "failed to add irq domain\n"); -+ return -ENODEV; -+ } -+ -+ ret = irq_alloc_domain_generic_chips(ipu->domain, 32, 1, "IPU", -+ handle_level_irq, 0, 0, 0); -+ if (ret < 0) { -+ dev_err(ipu->dev, "failed to alloc generic irq chips\n"); -+ irq_domain_remove(ipu->domain); -+ return ret; -+ } -+ -+ /* Mask and clear all interrupts */ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ ipu_cm_write(ipu, 0, IPU_INT_CTRL(i / 32)); -+ ipu_cm_write(ipu, ~unused[i / 32], IPU_INT_STAT(i / 32)); -+ } -+ -+ for (i = 0; i < IPU_NUM_IRQS; i += 32) { -+ gc = irq_get_domain_generic_chip(ipu->domain, i); -+ gc->reg_base = ipu->cm_reg; -+ gc->unused = unused[i / 32]; -+ ct = gc->chip_types; -+ ct->chip.irq_ack = irq_gc_ack_set_bit; -+ ct->chip.irq_mask = irq_gc_mask_clr_bit; -+ ct->chip.irq_unmask = irq_gc_mask_set_bit; -+ ct->regs.ack = IPU_INT_STAT(i / 32); -+ ct->regs.mask = IPU_INT_CTRL(i / 32); -+ } -+ -+ irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu); -+ irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler, -+ ipu); -+ -+ return 0; -+} -+ -+static void ipu_irq_exit(struct ipu_soc *ipu) -+{ -+ int i, irq; -+ -+ irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); -+ irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL); -+ -+ /* TODO: remove irq_domain_generic_chips */ -+ -+ for (i = 0; i < IPU_NUM_IRQS; i++) { -+ irq = irq_linear_revmap(ipu->domain, i); -+ if (irq) -+ irq_dispose_mapping(irq); -+ } -+ -+ irq_domain_remove(ipu->domain); -+} -+ -+void ipu_dump(struct ipu_soc *ipu) -+{ -+ int i; -+ -+ dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CONF)); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); -+ dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); -+ dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", -+ ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); -+ dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); -+ dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); -+ dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", -+ ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); -+ for (i = 0; i < 15; i++) -+ dev_dbg(ipu->dev, "IPU_INT_CTRL(%d) = \t%08X\n", i, -+ ipu_cm_read(ipu, IPU_INT_CTRL(i))); -+} -+EXPORT_SYMBOL_GPL(ipu_dump); -+ -+static int ipu_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct ipu_soc *ipu; -+ struct resource *res; -+ unsigned long ipu_base; -+ int ret, irq_sync, irq_err; -+ const struct ipu_devtype *devtype; -+ -+ devtype = of_device_get_match_data(&pdev->dev); -+ if (!devtype) -+ return -EINVAL; -+ -+ irq_sync = platform_get_irq(pdev, 0); -+ irq_err = platform_get_irq(pdev, 1); -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ dev_dbg(&pdev->dev, "irq_sync: %d irq_err: %d\n", -+ irq_sync, irq_err); -+ -+ if (!res || irq_sync < 0 || irq_err < 0) -+ return -ENODEV; -+ -+ ipu_base = res->start; -+ -+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL); -+ if (!ipu) -+ return -ENODEV; -+ -+ ipu->id = of_alias_get_id(np, "ipu"); -+ if (ipu->id < 0) -+ ipu->id = 0; -+ -+ if (of_device_is_compatible(np, "fsl,imx6qp-ipu") && -+ IS_ENABLED(CONFIG_DRM)) { -+ ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev, -+ "fsl,prg", ipu->id); -+ if (!ipu->prg_priv) -+ return -EPROBE_DEFER; -+ } -+ -+ ipu->devtype = devtype; -+ ipu->ipu_type = devtype->type; -+ -+ spin_lock_init(&ipu->lock); -+ mutex_init(&ipu->channel_lock); -+ INIT_LIST_HEAD(&ipu->channels); -+ -+ dev_dbg(&pdev->dev, "cm_reg: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs); -+ dev_dbg(&pdev->dev, "idmac: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS); -+ dev_dbg(&pdev->dev, "cpmem: 0x%08lx\n", -+ ipu_base + devtype->cpmem_ofs); -+ dev_dbg(&pdev->dev, "csi0: 0x%08lx\n", -+ ipu_base + devtype->csi0_ofs); -+ dev_dbg(&pdev->dev, "csi1: 0x%08lx\n", -+ ipu_base + devtype->csi1_ofs); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->ic_ofs); -+ dev_dbg(&pdev->dev, "disp0: 0x%08lx\n", -+ ipu_base + devtype->disp0_ofs); -+ dev_dbg(&pdev->dev, "disp1: 0x%08lx\n", -+ ipu_base + devtype->disp1_ofs); -+ dev_dbg(&pdev->dev, "srm: 0x%08lx\n", -+ ipu_base + devtype->srm_ofs); -+ dev_dbg(&pdev->dev, "tpm: 0x%08lx\n", -+ ipu_base + devtype->tpm_ofs); -+ dev_dbg(&pdev->dev, "dc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS); -+ dev_dbg(&pdev->dev, "ic: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS); -+ dev_dbg(&pdev->dev, "dmfc: 0x%08lx\n", -+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS); -+ dev_dbg(&pdev->dev, "vdi: 0x%08lx\n", -+ ipu_base + devtype->vdi_ofs); -+ -+ ipu->cm_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs, PAGE_SIZE); -+ ipu->idmac_reg = devm_ioremap(&pdev->dev, -+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS, -+ PAGE_SIZE); -+ -+ if (!ipu->cm_reg || !ipu->idmac_reg) -+ return -ENOMEM; -+ -+ ipu->clk = devm_clk_get(&pdev->dev, "bus"); -+ if (IS_ERR(ipu->clk)) { -+ ret = PTR_ERR(ipu->clk); -+ dev_err(&pdev->dev, "clk_get failed with %d", ret); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, ipu); -+ -+ ret = clk_prepare_enable(ipu->clk); -+ if (ret) { -+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret); -+ return ret; -+ } -+ -+ ipu->dev = &pdev->dev; -+ ipu->irq_sync = irq_sync; -+ ipu->irq_err = irq_err; -+ -+ ret = device_reset(&pdev->dev); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to reset: %d\n", ret); -+ goto out_failed_reset; -+ } -+ ret = ipu_memory_reset(ipu); -+ if (ret) -+ goto out_failed_reset; -+ -+ ret = ipu_irq_init(ipu); -+ if (ret) -+ goto out_failed_irq; -+ -+ /* Set MCU_T to divide MCU access window into 2 */ -+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), -+ IPU_DISP_GEN); -+ -+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk); -+ if (ret) -+ goto failed_submodules_init; -+ -+ ret = ipu_add_client_devices(ipu, ipu_base); -+ if (ret) { -+ dev_err(&pdev->dev, "adding client devices failed with %d\n", -+ ret); -+ goto failed_add_clients; -+ } -+ -+ dev_info(&pdev->dev, "%s probed\n", devtype->name); -+ -+ return 0; -+ -+failed_add_clients: -+ ipu_submodules_exit(ipu); -+failed_submodules_init: -+ ipu_irq_exit(ipu); -+out_failed_irq: -+out_failed_reset: -+ clk_disable_unprepare(ipu->clk); -+ return ret; -+} -+ -+static int ipu_remove(struct platform_device *pdev) -+{ -+ struct ipu_soc *ipu = platform_get_drvdata(pdev); -+ -+ platform_device_unregister_children(pdev); -+ ipu_submodules_exit(ipu); -+ ipu_irq_exit(ipu); -+ -+ clk_disable_unprepare(ipu->clk); -+ -+ return 0; -+} -+ -+static struct platform_driver imx_ipu_driver = { -+ .driver = { -+ .name = "imx-ipuv3", -+ .of_match_table = imx_ipu_dt_ids, -+ }, -+ .probe = ipu_probe, -+ .remove = ipu_remove, -+}; -+ -+static struct platform_driver * const drivers[] = { -+#if IS_ENABLED(CONFIG_DRM) -+ &ipu_pre_drv, -+ &ipu_prg_drv, -+#endif -+ &imx_ipu_driver, -+}; -+ -+static int __init imx_ipu_init(void) -+{ -+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_init(imx_ipu_init); -+ -+static void __exit imx_ipu_exit(void) -+{ -+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_exit(imx_ipu_exit); -+ -+MODULE_ALIAS("platform:imx-ipuv3"); -+MODULE_DESCRIPTION("i.MX IPU v3 driver"); -+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); -+MODULE_LICENSE("GPL"); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c -@@ -0,0 +1,976 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#include <linux/types.h> -+#include <linux/bitrev.h> -+#include <linux/io.h> -+#include <linux/sizes.h> -+#include <drm/drm_fourcc.h> -+#include "ipu-prv.h" -+ -+struct ipu_cpmem_word { -+ u32 data[5]; -+ u32 res[3]; -+}; -+ -+struct ipu_ch_param { -+ struct ipu_cpmem_word word[2]; -+}; -+ -+struct ipu_cpmem { -+ struct ipu_ch_param __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size)) -+ -+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22) -+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22) -+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4) -+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1) -+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1) -+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14) -+ -+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10) -+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9) -+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13) -+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12) -+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1) -+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1) -+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12) -+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11) -+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10) -+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7) -+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10) -+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1) -+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1) -+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7) -+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1) -+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1) -+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3) -+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2) -+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1) -+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3) -+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2) -+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1) -+#define IPU_FIELD_ROT_HF_VF IPU_CPMEM_WORD(0, 119, 3) -+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1) -+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1) -+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1) -+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1) -+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1) -+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13) -+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12) -+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29) -+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29) -+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20) -+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7) -+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4) -+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1) -+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3) -+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2) -+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7) -+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14) -+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3) -+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3) -+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3) -+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3) -+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5) -+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5) -+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5) -+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5) -+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1) -+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1) -+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1) -+ -+static inline struct ipu_ch_param __iomem * -+ipu_get_cpmem(struct ipuv3_channel *ch) -+{ -+ struct ipu_cpmem *cpmem = ch->ipu->cpmem_priv; -+ -+ return cpmem->base + ch->num; -+} -+ -+static void ipu_ch_param_write_field(struct ipuv3_channel *ch, u32 wbs, u32 v) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = readl(&base->word[word].data[i]); -+ val &= ~(mask << ofs); -+ val |= v << ofs; -+ writel(val, &base->word[word].data[i]); -+ -+ if ((bit + size - 1) / 32 > i) { -+ val = readl(&base->word[word].data[i + 1]); -+ val &= ~(mask >> (ofs ? (32 - ofs) : 0)); -+ val |= v >> (ofs ? (32 - ofs) : 0); -+ writel(val, &base->word[word].data[i + 1]); -+ } -+} -+ -+static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs) -+{ -+ struct ipu_ch_param __iomem *base = ipu_get_cpmem(ch); -+ u32 bit = (wbs >> 8) % 160; -+ u32 size = wbs & 0xff; -+ u32 word = (wbs >> 8) / 160; -+ u32 i = bit / 32; -+ u32 ofs = bit % 32; -+ u32 mask = (1 << size) - 1; -+ u32 val = 0; -+ -+ pr_debug("%s %d %d %d\n", __func__, word, bit , size); -+ -+ val = (readl(&base->word[word].data[i]) >> ofs) & mask; -+ -+ if ((bit + size - 1) / 32 > i) { -+ u32 tmp; -+ -+ tmp = readl(&base->word[word].data[i + 1]); -+ tmp &= mask >> (ofs ? (32 - ofs) : 0); -+ val |= tmp << (ofs ? (32 - ofs) : 0); -+ } -+ -+ return val; -+} -+ -+/* -+ * The V4L2 spec defines packed RGB formats in memory byte order, which from -+ * point of view of the IPU corresponds to little-endian words with the first -+ * component in the least significant bits. -+ * The DRM pixel formats and IPU internal representation are ordered the other -+ * way around, with the first named component ordered at the most significant -+ * bits. Further, V4L2 formats are not well defined: -+ * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html -+ * We choose the interpretation which matches GStreamer behavior. -+ */ -+static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat) -+{ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_RGB565: -+ /* -+ * Here we choose the 'corrected' interpretation of RGBP, a -+ * little-endian 16-bit word with the red component at the most -+ * significant bits: -+ * g[2:0]b[4:0] r[4:0]g[5:3] <=> [16:0] R:G:B -+ */ -+ return DRM_FORMAT_RGB565; -+ case V4L2_PIX_FMT_BGR24: -+ /* B G R <=> [24:0] R:G:B */ -+ return DRM_FORMAT_RGB888; -+ case V4L2_PIX_FMT_RGB24: -+ /* R G B <=> [24:0] B:G:R */ -+ return DRM_FORMAT_BGR888; -+ case V4L2_PIX_FMT_BGR32: -+ /* B G R A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_RGB32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ABGR32: -+ /* B G R A <=> [32:0] A:R:G:B */ -+ return DRM_FORMAT_ARGB8888; -+ case V4L2_PIX_FMT_XBGR32: -+ /* B G R X <=> [32:0] X:R:G:B */ -+ return DRM_FORMAT_XRGB8888; -+ case V4L2_PIX_FMT_BGRA32: -+ /* A B G R <=> [32:0] R:G:B:A */ -+ return DRM_FORMAT_RGBA8888; -+ case V4L2_PIX_FMT_BGRX32: -+ /* X B G R <=> [32:0] R:G:B:X */ -+ return DRM_FORMAT_RGBX8888; -+ case V4L2_PIX_FMT_RGBA32: -+ /* R G B A <=> [32:0] A:B:G:R */ -+ return DRM_FORMAT_ABGR8888; -+ case V4L2_PIX_FMT_RGBX32: -+ /* R G B X <=> [32:0] X:B:G:R */ -+ return DRM_FORMAT_XBGR8888; -+ case V4L2_PIX_FMT_ARGB32: -+ /* A R G B <=> [32:0] B:G:R:A */ -+ return DRM_FORMAT_BGRA8888; -+ case V4L2_PIX_FMT_XRGB32: -+ /* X R G B <=> [32:0] B:G:R:X */ -+ return DRM_FORMAT_BGRX8888; -+ case V4L2_PIX_FMT_UYVY: -+ return DRM_FORMAT_UYVY; -+ case V4L2_PIX_FMT_YUYV: -+ return DRM_FORMAT_YUYV; -+ case V4L2_PIX_FMT_YUV420: -+ return DRM_FORMAT_YUV420; -+ case V4L2_PIX_FMT_YUV422P: -+ return DRM_FORMAT_YUV422; -+ case V4L2_PIX_FMT_YVU420: -+ return DRM_FORMAT_YVU420; -+ case V4L2_PIX_FMT_NV12: -+ return DRM_FORMAT_NV12; -+ case V4L2_PIX_FMT_NV16: -+ return DRM_FORMAT_NV16; -+ } -+ -+ return -EINVAL; -+} -+ -+void ipu_cpmem_zero(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ void __iomem *base = p; -+ int i; -+ -+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++) -+ writel(0, base + i * sizeof(u32)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_zero); -+ -+void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_FW, xres - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_FH, yres - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_resolution); -+ -+void ipu_cpmem_skip_odd_chroma_rows(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_RDRW, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_skip_odd_chroma_rows); -+ -+void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, stride - 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_stride); -+ -+void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ u32 val; -+ -+ if (ipu->ipu_type == IPUV3EX) -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(ch->num)); -+ val |= 1 << (ch->num % 32); -+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(ch->num)); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority); -+ -+void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf) -+{ -+ WARN_ON_ONCE(buf & 0x7); -+ -+ if (bufnum) -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA1, buf >> 3); -+ else -+ ipu_ch_param_write_field(ch, IPU_FIELD_EBA0, buf >> 3); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer); -+ -+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off) -+{ -+ WARN_ON_ONCE((u_off & 0x7) || (v_off & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset); -+ -+void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride, -+ u32 pixelformat) -+{ -+ u32 ilo, sly, sluv; -+ -+ if (stride < 0) { -+ stride = -stride; -+ ilo = 0x100000 - (stride / 8); -+ } else { -+ ilo = stride / 8; -+ } -+ -+ sly = (stride * 2) - 1; -+ -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ case V4L2_PIX_FMT_YVU420: -+ sluv = stride / 2 - 1; -+ break; -+ case V4L2_PIX_FMT_NV12: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ sluv = stride - 1; -+ break; -+ case V4L2_PIX_FMT_NV16: -+ sluv = stride * 2 - 1; -+ break; -+ default: -+ sluv = 0; -+ break; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ILO, ilo); -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLY, sly); -+ if (sluv) -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, sluv); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_interlaced_scan); -+ -+void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id) -+{ -+ id &= 0x3; -+ ipu_ch_param_write_field(ch, IPU_FIELD_ID, id); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id); -+ -+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch) -+{ -+ return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize); -+ -+void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1); -+}; -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_burstsize); -+ -+void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch) -+{ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BM, 1); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_block_mode); -+ -+void ipu_cpmem_set_rotation(struct ipuv3_channel *ch, -+ enum ipu_rotate_mode rot) -+{ -+ u32 temp_rot = bitrev8(rot) >> 5; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ROT_HF_VF, temp_rot); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_rotation); -+ -+int ipu_cpmem_set_format_rgb(struct ipuv3_channel *ch, -+ const struct ipu_rgb *rgb) -+{ -+ int bpp = 0, npb = 0, ro, go, bo, to; -+ -+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset; -+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset; -+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset; -+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID0, rgb->red.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS0, ro); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID1, rgb->green.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS1, go); -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID2, rgb->blue.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS2, bo); -+ -+ if (rgb->transp.length) { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, -+ rgb->transp.length - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, to); -+ } else { -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_ch_param_write_field(ch, IPU_FIELD_OFS3, -+ rgb->bits_per_pixel); -+ } -+ -+ switch (rgb->bits_per_pixel) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 7); /* rgb mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb); -+ -+int ipu_cpmem_set_format_passthrough(struct ipuv3_channel *ch, int width) -+{ -+ int bpp = 0, npb = 0; -+ -+ switch (width) { -+ case 32: -+ bpp = 0; -+ npb = 15; -+ break; -+ case 24: -+ bpp = 1; -+ npb = 19; -+ break; -+ case 16: -+ bpp = 3; -+ npb = 31; -+ break; -+ case 8: -+ bpp = 5; -+ npb = 63; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, bpp); -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, npb); -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 6); /* raw mode */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough); -+ -+void ipu_cpmem_set_yuv_interleaved(struct ipuv3_channel *ch, u32 pixel_format) -+{ -+ switch (pixel_format) { -+ case V4L2_PIX_FMT_UYVY: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ case V4L2_PIX_FMT_YUYV: -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8);/* pix fmt */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31);/* burst size */ -+ break; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_interleaved); -+ -+void ipu_cpmem_set_yuv_planar_full(struct ipuv3_channel *ch, -+ unsigned int uv_stride, -+ unsigned int u_offset, unsigned int v_offset) -+{ -+ WARN_ON_ONCE((u_offset & 0x7) || (v_offset & 0x7)); -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_SLUV, uv_stride - 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_offset / 8); -+ ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_offset / 8); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full); -+ -+static const struct ipu_rgb def_xrgb_32 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_xbgr_32 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 24, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgbx_32 = { -+ .red = { .offset = 24, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 8, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_bgrx_32 = { -+ .red = { .offset = 8, .length = 8, }, -+ .green = { .offset = 16, .length = 8, }, -+ .blue = { .offset = 24, .length = 8, }, -+ .transp = { .offset = 0, .length = 8, }, -+ .bits_per_pixel = 32, -+}; -+ -+static const struct ipu_rgb def_rgb_24 = { -+ .red = { .offset = 16, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 0, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_bgr_24 = { -+ .red = { .offset = 0, .length = 8, }, -+ .green = { .offset = 8, .length = 8, }, -+ .blue = { .offset = 16, .length = 8, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 24, -+}; -+ -+static const struct ipu_rgb def_rgb_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 6, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 0, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16 = { -+ .red = { .offset = 10, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 0, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_argb_16_4444 = { -+ .red = { .offset = 8, .length = 4, }, -+ .green = { .offset = 4, .length = 4, }, -+ .blue = { .offset = 0, .length = 4, }, -+ .transp = { .offset = 12, .length = 4, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_abgr_16 = { -+ .red = { .offset = 0, .length = 5, }, -+ .green = { .offset = 5, .length = 5, }, -+ .blue = { .offset = 10, .length = 5, }, -+ .transp = { .offset = 15, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_rgba_16 = { -+ .red = { .offset = 11, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 1, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+static const struct ipu_rgb def_bgra_16 = { -+ .red = { .offset = 1, .length = 5, }, -+ .green = { .offset = 6, .length = 5, }, -+ .blue = { .offset = 11, .length = 5, }, -+ .transp = { .offset = 0, .length = 1, }, -+ .bits_per_pixel = 16, -+}; -+ -+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y)) -+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 4) + \ -+ (pix->width * ((y) / 2) / 2) + (x) / 2) -+#define U2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define V2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * pix->height / 2) + \ -+ (pix->width * (y) / 2) + (x) / 2) -+#define UV_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * ((y) / 2)) + (x)) -+#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \ -+ (pix->width * y) + (x)) -+ -+#define NUM_ALPHA_CHANNELS 7 -+ -+/* See Table 37-12. Alpha channels mapping. */ -+static int ipu_channel_albm(int ch_num) -+{ -+ switch (ch_num) { -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: return 1; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: return 2; -+ case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3; -+ case IPUV3_CHANNEL_MEM_BG_SYNC: return 4; -+ case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5; -+ case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch) -+{ -+ struct ipu_soc *ipu = ch->ipu; -+ int albm; -+ u32 val; -+ -+ albm = ipu_channel_albm(ch->num); -+ if (albm < 0) -+ return; -+ -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1); -+ ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm); -+ ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1); -+ -+ val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); -+ val |= BIT(ch->num); -+ ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA); -+} -+ -+int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc) -+{ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_YUV420: -+ case DRM_FORMAT_YVU420: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 2); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV422: -+ case DRM_FORMAT_YVU422: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 1); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUV444: -+ case DRM_FORMAT_YVU444: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV12: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 4); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_NV16: -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 3); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_UYVY: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0xA); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_YUYV: -+ /* bits/pixel */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_BPP, 3); -+ /* pix format */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_PFS, 0x8); -+ /* burst size */ -+ ipu_ch_param_write_field(ch, IPU_FIELD_NPB, 31); -+ break; -+ case DRM_FORMAT_ABGR8888: -+ case DRM_FORMAT_XBGR8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xbgr_32); -+ break; -+ case DRM_FORMAT_ARGB8888: -+ case DRM_FORMAT_XRGB8888: -+ ipu_cpmem_set_format_rgb(ch, &def_xrgb_32); -+ break; -+ case DRM_FORMAT_RGBA8888: -+ case DRM_FORMAT_RGBX8888: -+ case DRM_FORMAT_RGBX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgbx_32); -+ break; -+ case DRM_FORMAT_BGRA8888: -+ case DRM_FORMAT_BGRX8888: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgrx_32); -+ break; -+ case DRM_FORMAT_BGR888: -+ case DRM_FORMAT_BGR888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_24); -+ break; -+ case DRM_FORMAT_RGB888: -+ case DRM_FORMAT_RGB888_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_24); -+ break; -+ case DRM_FORMAT_RGB565: -+ case DRM_FORMAT_RGB565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_rgb_16); -+ break; -+ case DRM_FORMAT_BGR565: -+ case DRM_FORMAT_BGR565_A8: -+ ipu_cpmem_set_format_rgb(ch, &def_bgr_16); -+ break; -+ case DRM_FORMAT_ARGB1555: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16); -+ break; -+ case DRM_FORMAT_ABGR1555: -+ ipu_cpmem_set_format_rgb(ch, &def_abgr_16); -+ break; -+ case DRM_FORMAT_RGBA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_rgba_16); -+ break; -+ case DRM_FORMAT_BGRA5551: -+ ipu_cpmem_set_format_rgb(ch, &def_bgra_16); -+ break; -+ case DRM_FORMAT_ARGB4444: -+ ipu_cpmem_set_format_rgb(ch, &def_argb_16_4444); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ switch (drm_fourcc) { -+ case DRM_FORMAT_RGB565_A8: -+ case DRM_FORMAT_BGR565_A8: -+ case DRM_FORMAT_RGB888_A8: -+ case DRM_FORMAT_BGR888_A8: -+ case DRM_FORMAT_RGBX8888_A8: -+ case DRM_FORMAT_BGRX8888_A8: -+ ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7); -+ ipu_cpmem_set_separate_alpha(ch); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt); -+ -+int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image) -+{ -+ struct v4l2_pix_format *pix = &image->pix; -+ int offset, u_offset, v_offset; -+ int ret = 0; -+ -+ pr_debug("%s: resolution: %dx%d stride: %d\n", -+ __func__, pix->width, pix->height, -+ pix->bytesperline); -+ -+ ipu_cpmem_set_resolution(ch, image->rect.width, image->rect.height); -+ ipu_cpmem_set_stride(ch, pix->bytesperline); -+ -+ ipu_cpmem_set_fmt(ch, v4l2_pix_fmt_to_drm_fourcc(pix->pixelformat)); -+ -+ switch (pix->pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YVU420: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : V_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : U_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_YUV422P: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : U2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? -+ image->v_offset : V2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline / 2, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV12: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_NV16: -+ offset = Y_OFFSET(pix, image->rect.left, image->rect.top); -+ u_offset = image->u_offset ? -+ image->u_offset : UV2_OFFSET(pix, image->rect.left, -+ image->rect.top) - offset; -+ v_offset = image->v_offset ? image->v_offset : 0; -+ -+ ipu_cpmem_set_yuv_planar_full(ch, pix->bytesperline, -+ u_offset, v_offset); -+ break; -+ case V4L2_PIX_FMT_UYVY: -+ case V4L2_PIX_FMT_YUYV: -+ case V4L2_PIX_FMT_RGB565: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB32: -+ case V4L2_PIX_FMT_BGR32: -+ case V4L2_PIX_FMT_ABGR32: -+ case V4L2_PIX_FMT_XBGR32: -+ case V4L2_PIX_FMT_BGRA32: -+ case V4L2_PIX_FMT_BGRX32: -+ case V4L2_PIX_FMT_RGBA32: -+ case V4L2_PIX_FMT_RGBX32: -+ case V4L2_PIX_FMT_ARGB32: -+ case V4L2_PIX_FMT_XRGB32: -+ offset = image->rect.left * 4 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_RGB24: -+ case V4L2_PIX_FMT_BGR24: -+ offset = image->rect.left * 3 + -+ image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR8: -+ case V4L2_PIX_FMT_SGBRG8: -+ case V4L2_PIX_FMT_SGRBG8: -+ case V4L2_PIX_FMT_SRGGB8: -+ case V4L2_PIX_FMT_GREY: -+ offset = image->rect.left + image->rect.top * pix->bytesperline; -+ break; -+ case V4L2_PIX_FMT_SBGGR16: -+ case V4L2_PIX_FMT_SGBRG16: -+ case V4L2_PIX_FMT_SGRBG16: -+ case V4L2_PIX_FMT_SRGGB16: -+ case V4L2_PIX_FMT_Y16: -+ offset = image->rect.left * 2 + -+ image->rect.top * pix->bytesperline; -+ break; -+ default: -+ /* This should not happen */ -+ WARN_ON(1); -+ offset = 0; -+ ret = -EINVAL; -+ } -+ -+ ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset); -+ ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image); -+ -+void ipu_cpmem_dump(struct ipuv3_channel *ch) -+{ -+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(ch); -+ struct ipu_soc *ipu = ch->ipu; -+ int chno = ch->num; -+ -+ dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[0].data[0]), -+ readl(&p->word[0].data[1]), -+ readl(&p->word[0].data[2]), -+ readl(&p->word[0].data[3]), -+ readl(&p->word[0].data[4])); -+ dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", chno, -+ readl(&p->word[1].data[0]), -+ readl(&p->word[1].data[1]), -+ readl(&p->word[1].data[2]), -+ readl(&p->word[1].data[3]), -+ readl(&p->word[1].data[4])); -+ dev_dbg(ipu->dev, "PFS 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_PFS)); -+ dev_dbg(ipu->dev, "BPP 0x%x, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_BPP)); -+ dev_dbg(ipu->dev, "NPB 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_NPB)); -+ -+ dev_dbg(ipu->dev, "FW %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FW)); -+ dev_dbg(ipu->dev, "FH %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_FH)); -+ dev_dbg(ipu->dev, "EBA0 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA0) << 3); -+ dev_dbg(ipu->dev, "EBA1 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_EBA1) << 3); -+ dev_dbg(ipu->dev, "Stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SL)); -+ dev_dbg(ipu->dev, "scan_order %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SO)); -+ dev_dbg(ipu->dev, "uv_stride %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_SLUV)); -+ dev_dbg(ipu->dev, "u_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_UBO) << 3); -+ dev_dbg(ipu->dev, "v_offset 0x%x\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_VBO) << 3); -+ -+ dev_dbg(ipu->dev, "Width0 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID0)); -+ dev_dbg(ipu->dev, "Width1 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID1)); -+ dev_dbg(ipu->dev, "Width2 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID2)); -+ dev_dbg(ipu->dev, "Width3 %d+1, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_WID3)); -+ dev_dbg(ipu->dev, "Offset0 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS0)); -+ dev_dbg(ipu->dev, "Offset1 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS1)); -+ dev_dbg(ipu->dev, "Offset2 %d, ", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS2)); -+ dev_dbg(ipu->dev, "Offset3 %d\n", -+ ipu_ch_param_read_field(ch, IPU_FIELD_OFS3)); -+} -+EXPORT_SYMBOL_GPL(ipu_cpmem_dump); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_cpmem *cpmem; -+ -+ cpmem = devm_kzalloc(dev, sizeof(*cpmem), GFP_KERNEL); -+ if (!cpmem) -+ return -ENOMEM; -+ -+ ipu->cpmem_priv = cpmem; -+ -+ spin_lock_init(&cpmem->lock); -+ cpmem->base = devm_ioremap(dev, base, SZ_128K); -+ if (!cpmem->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CPMEM base: 0x%08lx remapped to %p\n", -+ base, cpmem->base); -+ cpmem->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_cpmem_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-csi.c -@@ -0,0 +1,821 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/videodev2.h> -+#include <uapi/linux/v4l2-mediabus.h> -+#include <linux/clk.h> -+#include <linux/clk-provider.h> -+#include <linux/clkdev.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_csi { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_ipu; /* IPU bus clock */ -+ spinlock_t lock; -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+/* CSI Register Offsets */ -+#define CSI_SENS_CONF 0x0000 -+#define CSI_SENS_FRM_SIZE 0x0004 -+#define CSI_ACT_FRM_SIZE 0x0008 -+#define CSI_OUT_FRM_CTRL 0x000c -+#define CSI_TST_CTRL 0x0010 -+#define CSI_CCIR_CODE_1 0x0014 -+#define CSI_CCIR_CODE_2 0x0018 -+#define CSI_CCIR_CODE_3 0x001c -+#define CSI_MIPI_DI 0x0020 -+#define CSI_SKIP 0x0024 -+#define CSI_CPD_CTRL 0x0028 -+#define CSI_CPD_RC(n) (0x002c + ((n)*4)) -+#define CSI_CPD_RS(n) (0x004c + ((n)*4)) -+#define CSI_CPD_GRC(n) (0x005c + ((n)*4)) -+#define CSI_CPD_GRS(n) (0x007c + ((n)*4)) -+#define CSI_CPD_GBC(n) (0x008c + ((n)*4)) -+#define CSI_CPD_GBS(n) (0x00Ac + ((n)*4)) -+#define CSI_CPD_BC(n) (0x00Bc + ((n)*4)) -+#define CSI_CPD_BS(n) (0x00Dc + ((n)*4)) -+#define CSI_CPD_OFFSET1 0x00ec -+#define CSI_CPD_OFFSET2 0x00f0 -+ -+/* CSI Register Fields */ -+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 -+#define CSI_SENS_CONF_DATA_FMT_MASK 0x00000700 -+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 0L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_YUYV 1L -+#define CSI_SENS_CONF_DATA_FMT_YUV422_UYVY 2L -+#define CSI_SENS_CONF_DATA_FMT_BAYER 3L -+#define CSI_SENS_CONF_DATA_FMT_RGB565 4L -+#define CSI_SENS_CONF_DATA_FMT_RGB555 5L -+#define CSI_SENS_CONF_DATA_FMT_RGB444 6L -+#define CSI_SENS_CONF_DATA_FMT_JPEG 7L -+ -+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 -+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 -+#define CSI_SENS_CONF_DATA_POL_SHIFT 2 -+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 -+#define CSI_SENS_CONF_SENS_PRTCL_MASK 0x00000070 -+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 -+#define CSI_SENS_CONF_PACK_TIGHT_SHIFT 7 -+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 11 -+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 -+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 -+ -+#define CSI_SENS_CONF_DIVRATIO_MASK 0x00ff0000 -+#define CSI_SENS_CONF_DATA_DEST_SHIFT 24 -+#define CSI_SENS_CONF_DATA_DEST_MASK 0x07000000 -+#define CSI_SENS_CONF_JPEG8_EN_SHIFT 27 -+#define CSI_SENS_CONF_JPEG_EN_SHIFT 28 -+#define CSI_SENS_CONF_FORCE_EOF_SHIFT 29 -+#define CSI_SENS_CONF_DATA_EN_POL_SHIFT 31 -+ -+#define CSI_DATA_DEST_IC 2 -+#define CSI_DATA_DEST_IDMAC 4 -+ -+#define CSI_CCIR_ERR_DET_EN 0x01000000 -+#define CSI_HORI_DOWNSIZE_EN 0x80000000 -+#define CSI_VERT_DOWNSIZE_EN 0x40000000 -+#define CSI_TEST_GEN_MODE_EN 0x01000000 -+ -+#define CSI_HSC_MASK 0x1fff0000 -+#define CSI_HSC_SHIFT 16 -+#define CSI_VSC_MASK 0x00000fff -+#define CSI_VSC_SHIFT 0 -+ -+#define CSI_TEST_GEN_R_MASK 0x000000ff -+#define CSI_TEST_GEN_R_SHIFT 0 -+#define CSI_TEST_GEN_G_MASK 0x0000ff00 -+#define CSI_TEST_GEN_G_SHIFT 8 -+#define CSI_TEST_GEN_B_MASK 0x00ff0000 -+#define CSI_TEST_GEN_B_SHIFT 16 -+ -+#define CSI_MAX_RATIO_SKIP_SMFC_MASK 0x00000007 -+#define CSI_MAX_RATIO_SKIP_SMFC_SHIFT 0 -+#define CSI_SKIP_SMFC_MASK 0x000000f8 -+#define CSI_SKIP_SMFC_SHIFT 3 -+#define CSI_ID_2_SKIP_MASK 0x00000300 -+#define CSI_ID_2_SKIP_SHIFT 8 -+ -+#define CSI_COLOR_FIRST_ROW_MASK 0x00000002 -+#define CSI_COLOR_FIRST_COMP_MASK 0x00000001 -+ -+/* MIPI CSI-2 data types */ -+#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */ -+#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */ -+#define MIPI_DT_YUV422 0x1e /* UYVY... */ -+#define MIPI_DT_RGB444 0x20 -+#define MIPI_DT_RGB555 0x21 -+#define MIPI_DT_RGB565 0x22 -+#define MIPI_DT_RGB666 0x23 -+#define MIPI_DT_RGB888 0x24 -+#define MIPI_DT_RAW6 0x28 -+#define MIPI_DT_RAW7 0x29 -+#define MIPI_DT_RAW8 0x2a -+#define MIPI_DT_RAW10 0x2b -+#define MIPI_DT_RAW12 0x2c -+#define MIPI_DT_RAW14 0x2d -+ -+/* -+ * Bitfield of CSI bus signal polarities and modes. -+ */ -+struct ipu_csi_bus_config { -+ unsigned data_width:4; -+ unsigned clk_mode:3; -+ unsigned ext_vsync:1; -+ unsigned vsync_pol:1; -+ unsigned hsync_pol:1; -+ unsigned pixclk_pol:1; -+ unsigned data_pol:1; -+ unsigned sens_clksrc:1; -+ unsigned pack_tight:1; -+ unsigned force_eof:1; -+ unsigned data_en_pol:1; -+ -+ unsigned data_fmt; -+ unsigned mipi_dt; -+}; -+ -+/* -+ * Enumeration of CSI data bus widths. -+ */ -+enum ipu_csi_data_width { -+ IPU_CSI_DATA_WIDTH_4 = 0, -+ IPU_CSI_DATA_WIDTH_8 = 1, -+ IPU_CSI_DATA_WIDTH_10 = 3, -+ IPU_CSI_DATA_WIDTH_12 = 5, -+ IPU_CSI_DATA_WIDTH_16 = 9, -+}; -+ -+/* -+ * Enumeration of CSI clock modes. -+ */ -+enum ipu_csi_clk_mode { -+ IPU_CSI_CLK_MODE_GATED_CLK, -+ IPU_CSI_CLK_MODE_NONGATED_CLK, -+ IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, -+ IPU_CSI_CLK_MODE_CCIR656_INTERLACED, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, -+ IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, -+}; -+ -+static inline u32 ipu_csi_read(struct ipu_csi *csi, unsigned offset) -+{ -+ return readl(csi->base + offset); -+} -+ -+static inline void ipu_csi_write(struct ipu_csi *csi, u32 value, -+ unsigned offset) -+{ -+ writel(value, csi->base + offset); -+} -+ -+/* -+ * Set mclk division ratio for generating test mode mclk. Only used -+ * for test generator. -+ */ -+static int ipu_csi_set_testgen_mclk(struct ipu_csi *csi, u32 pixel_clk, -+ u32 ipu_clk) -+{ -+ u32 temp; -+ int div_ratio; -+ -+ div_ratio = (ipu_clk / pixel_clk) - 1; -+ -+ if (div_ratio > 0xFF || div_ratio < 0) { -+ dev_err(csi->ipu->dev, -+ "value of pixel_clk extends normal range\n"); -+ return -EINVAL; -+ } -+ -+ temp = ipu_csi_read(csi, CSI_SENS_CONF); -+ temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; -+ ipu_csi_write(csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), -+ CSI_SENS_CONF); -+ -+ return 0; -+} -+ -+/* -+ * Find the CSI data format and data width for the given V4L2 media -+ * bus pixel format code. -+ */ -+static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code, -+ enum v4l2_mbus_type mbus_type) -+{ -+ switch (mbus_code) { -+ case MEDIA_BUS_FMT_BGR565_2X8_BE: -+ case MEDIA_BUS_FMT_BGR565_2X8_LE: -+ case MEDIA_BUS_FMT_RGB565_2X8_BE: -+ case MEDIA_BUS_FMT_RGB565_2X8_LE: -+ if (mbus_type == V4L2_MBUS_CSI2_DPHY) -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; -+ else -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RGB565; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB444; -+ cfg->mipi_dt = MIPI_DT_RGB444; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; -+ cfg->mipi_dt = MIPI_DT_RGB555; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; -+ cfg->mipi_dt = MIPI_DT_RGB888; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_YUYV8_2X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_YUV422; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_16; -+ break; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ case MEDIA_BUS_FMT_Y8_1X8: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE: -+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ case MEDIA_BUS_FMT_Y10_1X10: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW10; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_10; -+ break; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ case MEDIA_BUS_FMT_Y12_1X12: -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; -+ cfg->mipi_dt = MIPI_DT_RAW12; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_12; -+ break; -+ case MEDIA_BUS_FMT_JPEG_1X8: -+ /* TODO */ -+ cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_JPEG; -+ cfg->mipi_dt = MIPI_DT_RAW8; -+ cfg->data_width = IPU_CSI_DATA_WIDTH_8; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* translate alternate field mode based on given standard */ -+static inline enum v4l2_field -+ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std) -+{ -+ return (field != V4L2_FIELD_ALTERNATE) ? field : -+ ((std & V4L2_STD_525_60) ? -+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB); -+} -+ -+/* -+ * Fill a CSI bus config struct from mbus_config and mbus_framefmt. -+ */ -+static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ int ret; -+ -+ memset(csicfg, 0, sizeof(*csicfg)); -+ -+ ret = mbus_code_to_bus_cfg(csicfg, mbus_fmt->code, mbus_cfg->type); -+ if (ret < 0) -+ return ret; -+ -+ switch (mbus_cfg->type) { -+ case V4L2_MBUS_PARALLEL: -+ csicfg->ext_vsync = 1; -+ csicfg->vsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_VSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->hsync_pol = (mbus_cfg->flags & -+ V4L2_MBUS_HSYNC_ACTIVE_LOW) ? 1 : 0; -+ csicfg->pixclk_pol = (mbus_cfg->flags & -+ V4L2_MBUS_PCLK_SAMPLE_FALLING) ? 1 : 0; -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; -+ break; -+ case V4L2_MBUS_BT656: -+ csicfg->ext_vsync = 0; -+ if (V4L2_FIELD_HAS_BOTH(mbus_fmt->field) || -+ mbus_fmt->field == V4L2_FIELD_ALTERNATE) -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; -+ else -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; -+ break; -+ case V4L2_MBUS_CSI2_DPHY: -+ /* -+ * MIPI CSI-2 requires non gated clock mode, all other -+ * parameters are not applicable for MIPI CSI-2 bus. -+ */ -+ csicfg->clk_mode = IPU_CSI_CLK_MODE_NONGATED_CLK; -+ break; -+ default: -+ /* will never get here, keep compiler quiet */ -+ break; -+ } -+ -+ return 0; -+} -+ -+static int -+ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt, -+ v4l2_std_id std) -+{ -+ enum v4l2_field infield, outfield; -+ bool swap_fields; -+ -+ /* get translated field type of input and output */ -+ infield = ipu_csi_translate_field(infmt->field, std); -+ outfield = ipu_csi_translate_field(outfmt->field, std); -+ -+ /* -+ * Write the H-V-F codes the CSI will match against the -+ * incoming data for start/end of active and blanking -+ * field intervals. If input and output field types are -+ * sequential but not the same (one is SEQ_BT and the other -+ * is SEQ_TB), swap the F-bit so that the CSI will capture -+ * field 1 lines before field 0 lines. -+ */ -+ swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) && -+ V4L2_FIELD_IS_SEQUENTIAL(outfield) && -+ infield != outfield); -+ -+ if (!swap_fields) { -+ /* -+ * Field0BlankEnd = 110, Field0BlankStart = 010 -+ * Field0ActiveEnd = 100, Field0ActiveStart = 000 -+ * Field1BlankEnd = 111, Field1BlankStart = 011 -+ * Field1ActiveEnd = 101, Field1ActiveStart = 001 -+ */ -+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2); -+ } else { -+ dev_dbg(csi->ipu->dev, "capture field swap\n"); -+ -+ /* same as above but with F-bit inverted */ -+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2); -+ } -+ -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ -+ return 0; -+} -+ -+ -+int ipu_csi_init_interface(struct ipu_csi *csi, -+ const struct v4l2_mbus_config *mbus_cfg, -+ const struct v4l2_mbus_framefmt *infmt, -+ const struct v4l2_mbus_framefmt *outfmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 width, height, data = 0; -+ v4l2_std_id std; -+ int ret; -+ -+ ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt); -+ if (ret < 0) -+ return ret; -+ -+ /* set default sensor frame width and height */ -+ width = infmt->width; -+ height = infmt->height; -+ if (infmt->field == V4L2_FIELD_ALTERNATE) -+ height *= 2; -+ -+ /* Set the CSI_SENS_CONF register remaining fields */ -+ data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | -+ cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | -+ cfg.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | -+ cfg.vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | -+ cfg.hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | -+ cfg.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | -+ cfg.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | -+ cfg.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | -+ cfg.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | -+ cfg.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | -+ cfg.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, data, CSI_SENS_CONF); -+ -+ /* Set CCIR registers */ -+ -+ switch (cfg.clk_mode) { -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ ipu_csi_write(csi, 0x40030, CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ if (width == 720 && height == 480) { -+ std = V4L2_STD_NTSC; -+ height = 525; -+ } else if (width == 720 && height == 576) { -+ std = V4L2_STD_PAL; -+ height = 625; -+ } else { -+ dev_err(csi->ipu->dev, -+ "Unsupported interlaced video mode\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std); -+ if (ret) -+ goto out_unlock; -+ break; -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ ipu_csi_write(csi, 0x40030 | CSI_CCIR_ERR_DET_EN, -+ CSI_CCIR_CODE_1); -+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3); -+ break; -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ ipu_csi_write(csi, 0, CSI_CCIR_CODE_1); -+ break; -+ } -+ -+ /* Setup sensor frame size */ -+ ipu_csi_write(csi, (width - 1) | ((height - 1) << 16), -+ CSI_SENS_FRM_SIZE); -+ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ -+out_unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_init_interface); -+ -+bool ipu_csi_is_interlaced(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ u32 sensor_protocol; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ sensor_protocol = -+ (ipu_csi_read(csi, CSI_SENS_CONF) & -+ CSI_SENS_CONF_SENS_PRTCL_MASK) >> -+ CSI_SENS_CONF_SENS_PRTCL_SHIFT; -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ switch (sensor_protocol) { -+ case IPU_CSI_CLK_MODE_GATED_CLK: -+ case IPU_CSI_CLK_MODE_NONGATED_CLK: -+ case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR: -+ return false; -+ case IPU_CSI_CLK_MODE_CCIR656_INTERLACED: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR: -+ case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR: -+ return true; -+ default: -+ dev_err(csi->ipu->dev, -+ "CSI %d sensor protocol unsupported\n", csi->id); -+ return false; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_csi_is_interlaced); -+ -+void ipu_csi_get_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_ACT_FRM_SIZE); -+ w->width = (reg & 0xFFFF) + 1; -+ w->height = (reg >> 16 & 0xFFFF) + 1; -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ w->left = (reg & CSI_HSC_MASK) >> CSI_HSC_SHIFT; -+ w->top = (reg & CSI_VSC_MASK) >> CSI_VSC_SHIFT; -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get_window); -+ -+void ipu_csi_set_window(struct ipu_csi *csi, struct v4l2_rect *w) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ ipu_csi_write(csi, (w->width - 1) | ((w->height - 1) << 16), -+ CSI_ACT_FRM_SIZE); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HSC_MASK | CSI_VSC_MASK); -+ reg |= ((w->top << CSI_VSC_SHIFT) | (w->left << CSI_HSC_SHIFT)); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_window); -+ -+void ipu_csi_set_downsize(struct ipu_csi *csi, bool horiz, bool vert) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ reg = ipu_csi_read(csi, CSI_OUT_FRM_CTRL); -+ reg &= ~(CSI_HORI_DOWNSIZE_EN | CSI_VERT_DOWNSIZE_EN); -+ reg |= (horiz ? CSI_HORI_DOWNSIZE_EN : 0) | -+ (vert ? CSI_VERT_DOWNSIZE_EN : 0); -+ ipu_csi_write(csi, reg, CSI_OUT_FRM_CTRL); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_downsize); -+ -+void ipu_csi_set_test_generator(struct ipu_csi *csi, bool active, -+ u32 r_value, u32 g_value, u32 b_value, -+ u32 pix_clk) -+{ -+ unsigned long flags; -+ u32 ipu_clk = clk_get_rate(csi->clk_ipu); -+ u32 temp; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_TST_CTRL); -+ -+ if (!active) { -+ temp &= ~CSI_TEST_GEN_MODE_EN; -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } else { -+ /* Set sensb_mclk div_ratio */ -+ ipu_csi_set_testgen_mclk(csi, pix_clk, ipu_clk); -+ -+ temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | -+ CSI_TEST_GEN_B_MASK); -+ temp |= CSI_TEST_GEN_MODE_EN; -+ temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | -+ (g_value << CSI_TEST_GEN_G_SHIFT) | -+ (b_value << CSI_TEST_GEN_B_SHIFT); -+ ipu_csi_write(csi, temp, CSI_TST_CTRL); -+ } -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_test_generator); -+ -+int ipu_csi_set_mipi_datatype(struct ipu_csi *csi, u32 vc, -+ struct v4l2_mbus_framefmt *mbus_fmt) -+{ -+ struct ipu_csi_bus_config cfg; -+ unsigned long flags; -+ u32 temp; -+ int ret; -+ -+ if (vc > 3) -+ return -EINVAL; -+ -+ ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY); -+ if (ret < 0) -+ return ret; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_MIPI_DI); -+ temp &= ~(0xff << (vc * 8)); -+ temp |= (cfg.mipi_dt << (vc * 8)); -+ ipu_csi_write(csi, temp, CSI_MIPI_DI); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_mipi_datatype); -+ -+int ipu_csi_set_skip_smfc(struct ipu_csi *csi, u32 skip, -+ u32 max_ratio, u32 id) -+{ -+ unsigned long flags; -+ u32 temp; -+ -+ if (max_ratio > 5 || id > 3) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ temp = ipu_csi_read(csi, CSI_SKIP); -+ temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | -+ CSI_SKIP_SMFC_MASK); -+ temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | -+ (id << CSI_ID_2_SKIP_SHIFT) | -+ (skip << CSI_SKIP_SMFC_SHIFT); -+ ipu_csi_write(csi, temp, CSI_SKIP); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_skip_smfc); -+ -+int ipu_csi_set_dest(struct ipu_csi *csi, enum ipu_csi_dest csi_dest) -+{ -+ unsigned long flags; -+ u32 csi_sens_conf, dest; -+ -+ if (csi_dest == IPU_CSI_DEST_IDMAC) -+ dest = CSI_DATA_DEST_IDMAC; -+ else -+ dest = CSI_DATA_DEST_IC; /* IC or VDIC */ -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ csi_sens_conf = ipu_csi_read(csi, CSI_SENS_CONF); -+ csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; -+ csi_sens_conf |= (dest << CSI_SENS_CONF_DATA_DEST_SHIFT); -+ ipu_csi_write(csi, csi_sens_conf, CSI_SENS_CONF); -+ -+ spin_unlock_irqrestore(&csi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_set_dest); -+ -+int ipu_csi_enable(struct ipu_csi *csi) -+{ -+ ipu_module_enable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_enable); -+ -+int ipu_csi_disable(struct ipu_csi *csi) -+{ -+ ipu_module_disable(csi->ipu, csi->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_disable); -+ -+struct ipu_csi *ipu_csi_get(struct ipu_soc *ipu, int id) -+{ -+ unsigned long flags; -+ struct ipu_csi *csi, *ret; -+ -+ if (id > 1) -+ return ERR_PTR(-EINVAL); -+ -+ csi = ipu->csi_priv[id]; -+ ret = csi; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ -+ if (csi->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ csi->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&csi->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_csi_get); -+ -+void ipu_csi_put(struct ipu_csi *csi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&csi->lock, flags); -+ csi->inuse = false; -+ spin_unlock_irqrestore(&csi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_put); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_csi *csi; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); -+ if (!csi) -+ return -ENOMEM; -+ -+ ipu->csi_priv[id] = csi; -+ -+ spin_lock_init(&csi->lock); -+ csi->module = module; -+ csi->id = id; -+ csi->clk_ipu = clk_ipu; -+ csi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!csi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "CSI%d base: 0x%08lx remapped to %p\n", -+ id, base, csi->base); -+ csi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_csi_exit(struct ipu_soc *ipu, int id) -+{ -+} -+ -+void ipu_csi_dump(struct ipu_csi *csi) -+{ -+ dev_dbg(csi->ipu->dev, "CSI_SENS_CONF: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_CONF)); -+ dev_dbg(csi->ipu->dev, "CSI_SENS_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_SENS_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE: %08x\n", -+ ipu_csi_read(csi, CSI_ACT_FRM_SIZE)); -+ dev_dbg(csi->ipu->dev, "CSI_OUT_FRM_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_OUT_FRM_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_TST_CTRL: %08x\n", -+ ipu_csi_read(csi, CSI_TST_CTRL)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_1: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_1)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_2: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_2)); -+ dev_dbg(csi->ipu->dev, "CSI_CCIR_CODE_3: %08x\n", -+ ipu_csi_read(csi, CSI_CCIR_CODE_3)); -+ dev_dbg(csi->ipu->dev, "CSI_MIPI_DI: %08x\n", -+ ipu_csi_read(csi, CSI_MIPI_DI)); -+ dev_dbg(csi->ipu->dev, "CSI_SKIP: %08x\n", -+ ipu_csi_read(csi, CSI_SKIP)); -+} -+EXPORT_SYMBOL_GPL(ipu_csi_dump); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dc.c -@@ -0,0 +1,420 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2) -+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2) -+ -+#define DC_EVT_NF 0 -+#define DC_EVT_NL 1 -+#define DC_EVT_EOF 2 -+#define DC_EVT_NFIELD 3 -+#define DC_EVT_EOL 4 -+#define DC_EVT_EOFIELD 5 -+#define DC_EVT_NEW_ADDR 6 -+#define DC_EVT_NEW_CHAN 7 -+#define DC_EVT_NEW_DATA 8 -+ -+#define DC_EVT_NEW_ADDR_W_0 0 -+#define DC_EVT_NEW_ADDR_W_1 1 -+#define DC_EVT_NEW_CHAN_W_0 2 -+#define DC_EVT_NEW_CHAN_W_1 3 -+#define DC_EVT_NEW_DATA_W_0 4 -+#define DC_EVT_NEW_DATA_W_1 5 -+#define DC_EVT_NEW_ADDR_R_0 6 -+#define DC_EVT_NEW_ADDR_R_1 7 -+#define DC_EVT_NEW_CHAN_R_0 8 -+#define DC_EVT_NEW_CHAN_R_1 9 -+#define DC_EVT_NEW_DATA_R_0 10 -+#define DC_EVT_NEW_DATA_R_1 11 -+ -+#define DC_WR_CH_CONF 0x0 -+#define DC_WR_CH_ADDR 0x4 -+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2) -+ -+#define DC_GEN 0xd4 -+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4) -+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4) -+#define DC_STAT 0x1c8 -+ -+#define WROD(lf) (0x18 | ((lf) << 1)) -+#define WRG 0x01 -+#define WCLK 0xc9 -+ -+#define SYNC_WAVE 0 -+#define NULL_WAVE (-1) -+ -+#define DC_GEN_SYNC_1_6_SYNC (2 << 1) -+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7) -+ -+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0) -+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0) -+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3) -+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3) -+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4) -+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9) -+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5) -+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5) -+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2) -+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3) -+ -+#define IPU_DC_NUM_CHANNELS 10 -+ -+struct ipu_dc_priv; -+ -+enum ipu_dc_map { -+ IPU_DC_MAP_RGB24, -+ IPU_DC_MAP_RGB565, -+ IPU_DC_MAP_GBR24, /* TVEv2 */ -+ IPU_DC_MAP_BGR666, -+ IPU_DC_MAP_LVDS666, -+ IPU_DC_MAP_BGR24, -+}; -+ -+struct ipu_dc { -+ /* The display interface number assigned to this dc channel */ -+ unsigned int di; -+ void __iomem *base; -+ struct ipu_dc_priv *priv; -+ int chno; -+ bool in_use; -+}; -+ -+struct ipu_dc_priv { -+ void __iomem *dc_reg; -+ void __iomem *dc_tmpl_reg; -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS]; -+ struct mutex mutex; -+ struct completion comp; -+ int use_count; -+}; -+ -+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_RL_CH(event)); -+ reg &= ~(0xffff << (16 * (event & 0x1))); -+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); -+ writel(reg, dc->base + DC_RL_CH(event)); -+} -+ -+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand, -+ int map, int wave, int glue, int sync, int stop) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ u32 reg1, reg2; -+ -+ if (opcode == WCLK) { -+ reg1 = (operand << 20) & 0xfff00000; -+ reg2 = operand >> 12 | opcode << 1 | stop << 9; -+ } else if (opcode == WRG) { -+ reg1 = sync | glue << 4 | ++wave << 11 | ((operand << 15) & 0xffff8000); -+ reg2 = operand >> 17 | opcode << 7 | stop << 9; -+ } else { -+ reg1 = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000); -+ reg2 = operand >> 12 | opcode << 4 | stop << 9; -+ } -+ writel(reg1, priv->dc_tmpl_reg + word * 8); -+ writel(reg2, priv->dc_tmpl_reg + word * 8 + 4); -+} -+ -+static int ipu_bus_format_to_map(u32 fmt) -+{ -+ switch (fmt) { -+ default: -+ WARN_ON(1); -+ /* fall-through */ -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return IPU_DC_MAP_RGB24; -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return IPU_DC_MAP_RGB565; -+ case MEDIA_BUS_FMT_GBR888_1X24: -+ return IPU_DC_MAP_GBR24; -+ case MEDIA_BUS_FMT_RGB666_1X18: -+ return IPU_DC_MAP_BGR666; -+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: -+ return IPU_DC_MAP_LVDS666; -+ case MEDIA_BUS_FMT_BGR888_1X24: -+ return IPU_DC_MAP_BGR24; -+ } -+} -+ -+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, -+ u32 bus_format, u32 width) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ int addr, sync; -+ u32 reg = 0; -+ int map; -+ -+ dc->di = ipu_di_get_num(di); -+ -+ map = ipu_bus_format_to_map(bus_format); -+ -+ /* -+ * In interlaced mode we need more counters to create the asymmetric -+ * per-field VSYNC signals. The pixel active signal synchronising DC -+ * to DI moves to signal generator #6 (see ipu-di.c). In progressive -+ * mode counter #5 is used. -+ */ -+ sync = interlaced ? 6 : 5; -+ -+ /* Reserve 5 microcode template words for each DI */ -+ if (dc->di) -+ addr = 5; -+ else -+ addr = 0; -+ -+ if (interlaced) { -+ dc_link_event(dc, DC_EVT_NL, addr, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } else { -+ dc_link_event(dc, DC_EVT_NL, addr + 2, 3); -+ dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); -+ dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); -+ -+ /* Init template microcode */ -+ dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); -+ dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); -+ dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); -+ dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); -+ } -+ -+ dc_link_event(dc, DC_EVT_NF, 0, 0); -+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_EOF, 0, 0); -+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0); -+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0); -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ if (interlaced) -+ reg |= DC_WR_CH_CONF_FIELD_MODE; -+ else -+ reg &= ~DC_WR_CH_CONF_FIELD_MODE; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+ -+ writel(0x0, dc->base + DC_WR_CH_ADDR); -+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di)); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_init_sync); -+ -+void ipu_dc_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable); -+ -+void ipu_dc_enable_channel(struct ipu_dc *dc) -+{ -+ u32 reg; -+ -+ reg = readl(dc->base + DC_WR_CH_CONF); -+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL; -+ writel(reg, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel); -+ -+void ipu_dc_disable_channel(struct ipu_dc *dc) -+{ -+ u32 val; -+ -+ val = readl(dc->base + DC_WR_CH_CONF); -+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; -+ writel(val, dc->base + DC_WR_CH_CONF); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel); -+ -+void ipu_dc_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_disable); -+ -+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map, -+ int byte_num, int offset, int mask) -+{ -+ int ptr = map * 3 + byte_num; -+ u32 reg; -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ reg &= ~(0xffff << (16 * (ptr & 0x1))); -+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr)); -+ -+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num))); -+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); -+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map) -+{ -+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map)); -+ -+ writel(reg & ~(0xffff << (16 * (map & 0x1))), -+ priv->dc_reg + DC_MAP_CONF_PTR(map)); -+} -+ -+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel) -+{ -+ struct ipu_dc_priv *priv = ipu->dc_priv; -+ struct ipu_dc *dc; -+ -+ if (channel >= IPU_DC_NUM_CHANNELS) -+ return ERR_PTR(-ENODEV); -+ -+ dc = &priv->channels[channel]; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (dc->in_use) { -+ mutex_unlock(&priv->mutex); -+ return ERR_PTR(-EBUSY); -+ } -+ -+ dc->in_use = true; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return dc; -+} -+EXPORT_SYMBOL_GPL(ipu_dc_get); -+ -+void ipu_dc_put(struct ipu_dc *dc) -+{ -+ struct ipu_dc_priv *priv = dc->priv; -+ -+ mutex_lock(&priv->mutex); -+ dc->in_use = false; -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dc_put); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long template_base) -+{ -+ struct ipu_dc_priv *priv; -+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c, -+ 0x78, 0, 0x94, 0xb4}; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE); -+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE); -+ if (!priv->dc_reg || !priv->dc_tmpl_reg) -+ return -ENOMEM; -+ -+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) { -+ priv->channels[i].chno = i; -+ priv->channels[i].priv = priv; -+ priv->channels[i].base = priv->dc_reg + channel_offsets[i]; -+ } -+ -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) | -+ DC_WR_CH_CONF_PROG_DI_ID, -+ priv->channels[1].base + DC_WR_CH_CONF); -+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0), -+ priv->channels[5].base + DC_WR_CH_CONF); -+ -+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, -+ priv->dc_reg + DC_GEN); -+ -+ ipu->dc_priv = priv; -+ -+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n", -+ base, template_base); -+ -+ /* rgb24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */ -+ -+ /* rgb565 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565); -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */ -+ -+ /* gbr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_GBR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 2, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 1, 7, 0xff); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_GBR24, 0, 23, 0xff); /* red */ -+ -+ /* bgr666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 1, 11, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR666, 2, 17, 0xfc); /* red */ -+ -+ /* lvds666 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_LVDS666); -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 0, 5, 0xfc); /* blue */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 1, 13, 0xfc); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_LVDS666, 2, 21, 0xfc); /* red */ -+ -+ /* bgr24 */ -+ ipu_dc_map_clear(priv, IPU_DC_MAP_BGR24); -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 2, 7, 0xff); /* red */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 1, 15, 0xff); /* green */ -+ ipu_dc_map_config(priv, IPU_DC_MAP_BGR24, 0, 23, 0xff); /* blue */ -+ -+ return 0; -+} -+ -+void ipu_dc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-di.c -@@ -0,0 +1,745 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+struct ipu_di { -+ void __iomem *base; -+ int id; -+ u32 module; -+ struct clk *clk_di; /* display input clock */ -+ struct clk *clk_ipu; /* IPU bus clock */ -+ struct clk *clk_di_pixel; /* resulting pixel clock */ -+ bool inuse; -+ struct ipu_soc *ipu; -+}; -+ -+static DEFINE_MUTEX(di_mutex); -+ -+struct di_sync_config { -+ int run_count; -+ int run_src; -+ int offset_count; -+ int offset_src; -+ int repeat_count; -+ int cnt_clr_src; -+ int cnt_polarity_gen_en; -+ int cnt_polarity_clr_src; -+ int cnt_polarity_trigger_src; -+ int cnt_up; -+ int cnt_down; -+}; -+ -+enum di_pins { -+ DI_PIN11 = 0, -+ DI_PIN12 = 1, -+ DI_PIN13 = 2, -+ DI_PIN14 = 3, -+ DI_PIN15 = 4, -+ DI_PIN16 = 5, -+ DI_PIN17 = 6, -+ DI_PIN_CS = 7, -+ -+ DI_PIN_SER_CLK = 0, -+ DI_PIN_SER_RS = 1, -+}; -+ -+enum di_sync_wave { -+ DI_SYNC_NONE = 0, -+ DI_SYNC_CLK = 1, -+ DI_SYNC_INT_HSYNC = 2, -+ DI_SYNC_HSYNC = 3, -+ DI_SYNC_VSYNC = 4, -+ DI_SYNC_DE = 6, -+ -+ DI_SYNC_CNT1 = 2, /* counter >= 2 only */ -+ DI_SYNC_CNT4 = 5, /* counter >= 5 only */ -+ DI_SYNC_CNT5 = 6, /* counter >= 6 only */ -+}; -+ -+#define SYNC_WAVE 0 -+ -+#define DI_GENERAL 0x0000 -+#define DI_BS_CLKGEN0 0x0004 -+#define DI_BS_CLKGEN1 0x0008 -+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1)) -+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1)) -+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2)) -+#define DI_SYNC_AS_GEN 0x0054 -+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen)) -+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set))) -+#define DI_SER_CONF 0x015c -+#define DI_SSC 0x0160 -+#define DI_POL 0x0164 -+#define DI_AW0 0x0168 -+#define DI_AW1 0x016c -+#define DI_SCR_CONF 0x0170 -+#define DI_STAT 0x0174 -+ -+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19) -+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16) -+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3) -+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0) -+ -+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29) -+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25) -+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12) -+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9) -+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16) -+#define DI_SW_GEN1_CNT_UP(x) (x) -+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000) -+ -+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24 -+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16 -+ -+#define DI_GEN_POLARITY_1 (1 << 0) -+#define DI_GEN_POLARITY_2 (1 << 1) -+#define DI_GEN_POLARITY_3 (1 << 2) -+#define DI_GEN_POLARITY_4 (1 << 3) -+#define DI_GEN_POLARITY_5 (1 << 4) -+#define DI_GEN_POLARITY_6 (1 << 5) -+#define DI_GEN_POLARITY_7 (1 << 6) -+#define DI_GEN_POLARITY_8 (1 << 7) -+#define DI_GEN_POLARITY_DISP_CLK (1 << 17) -+#define DI_GEN_DI_CLK_EXT (1 << 20) -+#define DI_GEN_DI_VSYNC_EXT (1 << 21) -+ -+#define DI_POL_DRDY_DATA_POLARITY (1 << 7) -+#define DI_POL_DRDY_POLARITY_15 (1 << 4) -+ -+#define DI_VSYNC_SEL_OFFSET 13 -+ -+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset) -+{ -+ return readl(di->base + offset); -+} -+ -+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) -+{ -+ writel(value, di->base + offset); -+} -+ -+static void ipu_di_data_wave_config(struct ipu_di *di, -+ int wave_gen, -+ int access_size, int component_size) -+{ -+ u32 reg; -+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | -+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+} -+ -+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin, -+ int set, int up, int down) -+{ -+ u32 reg; -+ -+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen)); -+ reg &= ~(0x3 << (di_pin * 2)); -+ reg |= set << (di_pin * 2); -+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen)); -+ -+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set)); -+} -+ -+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config, -+ int start, int count) -+{ -+ u32 reg; -+ int i; -+ -+ for (i = 0; i < count; i++) { -+ struct di_sync_config *c = &config[i]; -+ int wave_gen = start + i + 1; -+ -+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) || -+ (c->repeat_count >= 0x1000) || -+ (c->cnt_up >= 0x400) || -+ (c->cnt_down >= 0x400)) { -+ dev_err(di->ipu->dev, "DI%d counters out of range.\n", -+ di->id); -+ return; -+ } -+ -+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) | -+ DI_SW_GEN0_RUN_SRC(c->run_src) | -+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) | -+ DI_SW_GEN0_OFFSET_SRC(c->offset_src); -+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen)); -+ -+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) | -+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) | -+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC( -+ c->cnt_polarity_trigger_src) | -+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) | -+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) | -+ DI_SW_GEN1_CNT_UP(c->cnt_up); -+ -+ /* Enable auto reload */ -+ if (c->repeat_count == 0) -+ reg |= DI_SW_GEN1_AUTO_RELOAD; -+ -+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen)); -+ -+ reg = ipu_di_read(di, DI_STP_REP(wave_gen)); -+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1))); -+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1)); -+ ipu_di_write(di, reg, DI_STP_REP(wave_gen)); -+ } -+} -+ -+static void ipu_di_sync_config_interlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: internal VSYNC for each frame */ -+ .run_count = v_total * 2 - 1, -+ .run_src = 3, /* == counter 7 */ -+ }, { -+ /* PIN2: HSYNC waveform */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ }, { -+ /* PIN3: VSYNC waveform */ -+ .run_count = v_total - 1, -+ .run_src = 4, /* == counter 7 */ -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = 4, /* == counter 7 */ -+ .cnt_down = sig->mode.vsync_len * 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 4: Field */ -+ .run_count = v_total / 2, -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = h_total / 2, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = 2, -+ .cnt_clr_src = DI_SYNC_CNT1, -+ }, { -+ /* 5: Active lines */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = (sig->mode.vsync_len + -+ sig->mode.vback_porch) / 2, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive / 2, -+ .cnt_clr_src = DI_SYNC_CNT4, -+ }, { -+ /* 6: Active pixel, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = DI_SYNC_CNT5, -+ }, { -+ /* 7: Half line HSYNC */ -+ .run_count = h_total / 2 - 1, -+ .run_src = DI_SYNC_CLK, -+ } -+ }; -+ -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ -+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF); -+} -+ -+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, -+ struct ipu_di_signal_cfg *sig, int div) -+{ -+ u32 h_total = sig->mode.hactive + sig->mode.hsync_len + -+ sig->mode.hback_porch + sig->mode.hfront_porch; -+ u32 v_total = sig->mode.vactive + sig->mode.vsync_len + -+ sig->mode.vback_porch + sig->mode.vfront_porch; -+ struct di_sync_config cfg[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* PIN2: HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync, -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN3: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* 4: Line Active */ -+ .run_src = DI_SYNC_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = DI_SYNC_VSYNC, -+ } , { -+ /* 5: Pixel Active, referenced by DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 5, /* Line Active */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ } , { -+ /* unused */ -+ }, -+ }; -+ /* can't use #7 and #8 for line active and pixel active counters */ -+ struct di_sync_config cfg_vga[] = { -+ { -+ /* 1: INT_HSYNC */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ } , { -+ /* 2: VSYNC */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ } , { -+ /* 3: Line Active */ -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = sig->mode.vsync_len + -+ sig->mode.vback_porch, -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .repeat_count = sig->mode.vactive, -+ .cnt_clr_src = 3 /* VSYNC */, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* 5: Pixel Active signal to DC */ -+ .run_src = DI_SYNC_CLK, -+ .offset_count = sig->mode.hsync_len + -+ sig->mode.hback_porch, -+ .offset_src = DI_SYNC_CLK, -+ .repeat_count = sig->mode.hactive, -+ .cnt_clr_src = 4, /* Line Active */ -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on TQ MBa53 */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* PIN4: HSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = h_total - 1, -+ .run_src = DI_SYNC_CLK, -+ .offset_count = div * sig->v_to_h_sync + 18, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_CLK, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_CLK, -+ .cnt_down = sig->mode.hsync_len * 2, -+ } , { -+ /* PIN6: VSYNC for VGA via TVEv2 on i.MX53-QSB */ -+ .run_count = v_total - 1, -+ .run_src = DI_SYNC_INT_HSYNC, -+ .offset_count = 1, /* magic value from Freescale TVE driver */ -+ .offset_src = DI_SYNC_INT_HSYNC, -+ .cnt_polarity_gen_en = 1, -+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC, -+ .cnt_down = sig->mode.vsync_len * 2, -+ } , { -+ /* unused */ -+ }, -+ }; -+ -+ ipu_di_write(di, v_total - 1, DI_SCR_CONF); -+ if (sig->hsync_pin == 2 && sig->vsync_pin == 3) -+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg)); -+ else -+ ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); -+} -+ -+static void ipu_di_config_clock(struct ipu_di *di, -+ const struct ipu_di_signal_cfg *sig) -+{ -+ struct clk *clk; -+ unsigned clkgen0; -+ uint32_t val; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_EXT) { -+ /* -+ * CLKMODE_EXT means we must use the DI clock: this is -+ * needed for things like LVDS which needs to feed the -+ * DI and LDB with the same pixel clock. -+ */ -+ clk = di->clk_di; -+ -+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC) { -+ /* -+ * CLKMODE_SYNC means that we want the DI to be -+ * clocked at the same rate as the parent clock. -+ * This is needed (eg) for LDB which needs to be -+ * fed with the same pixel clock. We assume that -+ * the LDB clock has already been set correctly. -+ */ -+ clkgen0 = 1 << 4; -+ } else { -+ /* -+ * We can use the divider. We should really have -+ * a flag here indicating whether the bridge can -+ * cope with a fractional divider or not. For the -+ * time being, let's go for simplicitly and -+ * reliability. -+ */ -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } else { -+ /* -+ * For other interfaces, we can arbitarily select between -+ * the DI specific clock and the internal IPU clock. See -+ * DI_GENERAL bit 20. We select the IPU clock if it can -+ * give us a clock rate within 1% of the requested frequency, -+ * otherwise we use the DI clock. -+ */ -+ unsigned long rate, clkrate; -+ unsigned div, error; -+ -+ clkrate = clk_get_rate(di->clk_ipu); -+ div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ rate = clkrate / div; -+ -+ error = rate / (sig->mode.pixelclock / 1000); -+ -+ dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n", -+ rate, div, (signed)(error - 1000) / 10, error % 10); -+ -+ /* Allow a 1% error */ -+ if (error < 1010 && error >= 990) { -+ clk = di->clk_ipu; -+ -+ clkgen0 = div << 4; -+ } else { -+ unsigned long in_rate; -+ unsigned div; -+ -+ clk = di->clk_di; -+ -+ clk_set_rate(clk, sig->mode.pixelclock); -+ -+ in_rate = clk_get_rate(clk); -+ div = DIV_ROUND_CLOSEST(in_rate, sig->mode.pixelclock); -+ div = clamp(div, 1U, 255U); -+ -+ clkgen0 = div << 4; -+ } -+ } -+ -+ di->clk_di_pixel = clk; -+ -+ /* Set the divider */ -+ ipu_di_write(di, clkgen0, DI_BS_CLKGEN0); -+ -+ /* -+ * Set the high/low periods. Bits 24:16 give us the falling edge, -+ * and bits 8:0 give the rising edge. LSB is fraction, and is -+ * based on the divider above. We want a 50% duty cycle, so set -+ * the falling edge to be half the divider. -+ */ -+ ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1); -+ -+ /* Finally select the input clock */ -+ val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT; -+ if (clk == di->clk_di) -+ val |= DI_GEN_DI_CLK_EXT; -+ ipu_di_write(di, val, DI_GENERAL); -+ -+ dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n", -+ sig->mode.pixelclock, -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ clk == di->clk_di ? "DI" : "IPU", -+ clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4)); -+} -+ -+/* -+ * This function is called to adjust a video mode to IPU restrictions. -+ * It is meant to be called from drm crtc mode_fixup() methods. -+ */ -+int ipu_di_adjust_videomode(struct ipu_di *di, struct videomode *mode) -+{ -+ u32 diff; -+ -+ if (mode->vfront_porch >= 2) -+ return 0; -+ -+ diff = 2 - mode->vfront_porch; -+ -+ if (mode->vback_porch >= diff) { -+ mode->vfront_porch = 2; -+ mode->vback_porch -= diff; -+ } else if (mode->vsync_len > diff) { -+ mode->vfront_porch = 2; -+ mode->vsync_len = mode->vsync_len - diff; -+ } else { -+ dev_warn(di->ipu->dev, "failed to adjust videomode\n"); -+ return -EINVAL; -+ } -+ -+ dev_dbg(di->ipu->dev, "videomode adapted for IPU restrictions\n"); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_adjust_videomode); -+ -+static u32 ipu_di_gen_polarity(int pin) -+{ -+ switch (pin) { -+ case 1: -+ return DI_GEN_POLARITY_1; -+ case 2: -+ return DI_GEN_POLARITY_2; -+ case 3: -+ return DI_GEN_POLARITY_3; -+ case 4: -+ return DI_GEN_POLARITY_4; -+ case 5: -+ return DI_GEN_POLARITY_5; -+ case 6: -+ return DI_GEN_POLARITY_6; -+ case 7: -+ return DI_GEN_POLARITY_7; -+ case 8: -+ return DI_GEN_POLARITY_8; -+ } -+ return 0; -+} -+ -+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) -+{ -+ u32 reg; -+ u32 di_gen, vsync_cnt; -+ u32 div; -+ -+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", -+ di->id, sig->mode.hactive, sig->mode.vactive); -+ -+ dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n", -+ clk_get_rate(di->clk_ipu), -+ clk_get_rate(di->clk_di), -+ sig->mode.pixelclock); -+ -+ mutex_lock(&di_mutex); -+ -+ ipu_di_config_clock(di, sig); -+ -+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; -+ div = div / 16; /* Now divider is integer portion */ -+ -+ /* Setup pixel clock timing */ -+ /* Down time is half of period */ -+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1); -+ -+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1); -+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); -+ -+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT; -+ di_gen |= DI_GEN_DI_VSYNC_EXT; -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_INTERLACED) { -+ ipu_di_sync_config_interlaced(di, sig); -+ -+ /* set y_sel = 1 */ -+ di_gen |= 0x10000000; -+ -+ vsync_cnt = 3; -+ } else { -+ ipu_di_sync_config_noninterlaced(di, sig, div); -+ -+ vsync_cnt = 3; -+ if (di->id == 1) -+ /* -+ * TODO: change only for TVEv2, parallel display -+ * uses pin 2 / 3 -+ */ -+ if (!(sig->hsync_pin == 2 && sig->vsync_pin == 3)) -+ vsync_cnt = 6; -+ } -+ -+ if (sig->mode.flags & DISPLAY_FLAGS_HSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->hsync_pin); -+ if (sig->mode.flags & DISPLAY_FLAGS_VSYNC_HIGH) -+ di_gen |= ipu_di_gen_polarity(sig->vsync_pin); -+ -+ if (sig->clk_pol) -+ di_gen |= DI_GEN_POLARITY_DISP_CLK; -+ -+ ipu_di_write(di, di_gen, DI_GENERAL); -+ -+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002, -+ DI_SYNC_AS_GEN); -+ -+ reg = ipu_di_read(di, DI_POL); -+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); -+ -+ if (sig->enable_pol) -+ reg |= DI_POL_DRDY_POLARITY_15; -+ if (sig->data_pol) -+ reg |= DI_POL_DRDY_DATA_POLARITY; -+ -+ ipu_di_write(di, reg, DI_POL); -+ -+ mutex_unlock(&di_mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); -+ -+int ipu_di_enable(struct ipu_di *di) -+{ -+ int ret; -+ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ret = clk_prepare_enable(di->clk_di_pixel); -+ if (ret) -+ return ret; -+ -+ ipu_module_enable(di->ipu, di->module); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_enable); -+ -+int ipu_di_disable(struct ipu_di *di) -+{ -+ WARN_ON(IS_ERR(di->clk_di_pixel)); -+ -+ ipu_module_disable(di->ipu, di->module); -+ -+ clk_disable_unprepare(di->clk_di_pixel); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_di_disable); -+ -+int ipu_di_get_num(struct ipu_di *di) -+{ -+ return di->id; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get_num); -+ -+static DEFINE_MUTEX(ipu_di_lock); -+ -+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp) -+{ -+ struct ipu_di *di; -+ -+ if (disp > 1) -+ return ERR_PTR(-EINVAL); -+ -+ di = ipu->di_priv[disp]; -+ -+ mutex_lock(&ipu_di_lock); -+ -+ if (di->inuse) { -+ di = ERR_PTR(-EBUSY); -+ goto out; -+ } -+ -+ di->inuse = true; -+out: -+ mutex_unlock(&ipu_di_lock); -+ -+ return di; -+} -+EXPORT_SYMBOL_GPL(ipu_di_get); -+ -+void ipu_di_put(struct ipu_di *di) -+{ -+ mutex_lock(&ipu_di_lock); -+ -+ di->inuse = false; -+ -+ mutex_unlock(&ipu_di_lock); -+} -+EXPORT_SYMBOL_GPL(ipu_di_put); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, -+ u32 module, struct clk *clk_ipu) -+{ -+ struct ipu_di *di; -+ -+ if (id > 1) -+ return -ENODEV; -+ -+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); -+ if (!di) -+ return -ENOMEM; -+ -+ ipu->di_priv[id] = di; -+ -+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0"); -+ if (IS_ERR(di->clk_di)) -+ return PTR_ERR(di->clk_di); -+ -+ di->module = module; -+ di->id = id; -+ di->clk_ipu = clk_ipu; -+ di->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!di->base) -+ return -ENOMEM; -+ -+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0); -+ -+ dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", -+ id, base, di->base); -+ di->inuse = false; -+ di->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_di_exit(struct ipu_soc *ipu, int id) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dmfc.c -@@ -0,0 +1,214 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DMFC_RD_CHAN 0x0000 -+#define DMFC_WR_CHAN 0x0004 -+#define DMFC_WR_CHAN_DEF 0x0008 -+#define DMFC_DP_CHAN 0x000c -+#define DMFC_DP_CHAN_DEF 0x0010 -+#define DMFC_GENERAL1 0x0014 -+#define DMFC_GENERAL2 0x0018 -+#define DMFC_IC_CTRL 0x001c -+#define DMFC_WR_CHAN_ALT 0x0020 -+#define DMFC_WR_CHAN_DEF_ALT 0x0024 -+#define DMFC_DP_CHAN_ALT 0x0028 -+#define DMFC_DP_CHAN_DEF_ALT 0x002c -+#define DMFC_GENERAL1_ALT 0x0030 -+#define DMFC_STAT 0x0034 -+ -+#define DMFC_WR_CHAN_1_28 0 -+#define DMFC_WR_CHAN_2_41 8 -+#define DMFC_WR_CHAN_1C_42 16 -+#define DMFC_WR_CHAN_2C_43 24 -+ -+#define DMFC_DP_CHAN_5B_23 0 -+#define DMFC_DP_CHAN_5F_27 8 -+#define DMFC_DP_CHAN_6B_24 16 -+#define DMFC_DP_CHAN_6F_29 24 -+ -+struct dmfc_channel_data { -+ int ipu_channel; -+ unsigned long channel_reg; -+ unsigned long shift; -+ unsigned eot_shift; -+ unsigned max_fifo_lines; -+}; -+ -+static const struct dmfc_channel_data dmfcdata[] = { -+ { -+ .ipu_channel = IPUV3_CHANNEL_MEM_BG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5B_23, -+ .eot_shift = 20, -+ .max_fifo_lines = 3, -+ }, { -+ .ipu_channel = 24, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6B_24, -+ .eot_shift = 22, -+ .max_fifo_lines = 1, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_FG_SYNC, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_5F_27, -+ .eot_shift = 21, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = IPUV3_CHANNEL_MEM_DC_SYNC, -+ .channel_reg = DMFC_WR_CHAN, -+ .shift = DMFC_WR_CHAN_1_28, -+ .eot_shift = 16, -+ .max_fifo_lines = 2, -+ }, { -+ .ipu_channel = 29, -+ .channel_reg = DMFC_DP_CHAN, -+ .shift = DMFC_DP_CHAN_6F_29, -+ .eot_shift = 23, -+ .max_fifo_lines = 1, -+ }, -+}; -+ -+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata) -+ -+struct ipu_dmfc_priv; -+ -+struct dmfc_channel { -+ unsigned slots; -+ struct ipu_soc *ipu; -+ struct ipu_dmfc_priv *priv; -+ const struct dmfc_channel_data *data; -+}; -+ -+struct ipu_dmfc_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ struct dmfc_channel channels[DMFC_NUM_CHANNELS]; -+ struct mutex mutex; -+ void __iomem *base; -+ int use_count; -+}; -+ -+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel); -+ -+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel); -+ -+void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width) -+{ -+ struct ipu_dmfc_priv *priv = dmfc->priv; -+ u32 dmfc_gen1; -+ -+ mutex_lock(&priv->mutex); -+ -+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1); -+ -+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines) -+ dmfc_gen1 |= 1 << dmfc->data->eot_shift; -+ else -+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift); -+ -+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_config_wait4eot); -+ -+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel) -+{ -+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv; -+ int i; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) -+ if (dmfcdata[i].ipu_channel == ipu_channel) -+ return &priv->channels[i]; -+ return ERR_PTR(-ENODEV); -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_get); -+ -+void ipu_dmfc_put(struct dmfc_channel *dmfc) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_dmfc_put); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk) -+{ -+ struct ipu_dmfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ priv->dev = dev; -+ priv->ipu = ipu; -+ mutex_init(&priv->mutex); -+ -+ ipu->dmfc_priv = priv; -+ -+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) { -+ priv->channels[i].priv = priv; -+ priv->channels[i].ipu = ipu; -+ priv->channels[i].data = &dmfcdata[i]; -+ -+ if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC || -+ dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC) -+ priv->channels[i].slots = 2; -+ } -+ -+ writel(0x00000050, priv->base + DMFC_WR_CHAN); -+ writel(0x00005654, priv->base + DMFC_DP_CHAN); -+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF); -+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF); -+ writel(0x00000003, priv->base + DMFC_GENERAL1); -+ -+ return 0; -+} -+ -+void ipu_dmfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-dp.c -@@ -0,0 +1,357 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/export.h> -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/io.h> -+#include <linux/err.h> -+ -+#include <video/imx-ipu-v3.h> -+#include "ipu-prv.h" -+ -+#define DP_SYNC 0 -+#define DP_ASYNC0 0x60 -+#define DP_ASYNC1 0xBC -+ -+#define DP_COM_CONF 0x0 -+#define DP_GRAPH_WIND_CTRL 0x0004 -+#define DP_FG_POS 0x0008 -+#define DP_CSC_A_0 0x0044 -+#define DP_CSC_A_1 0x0048 -+#define DP_CSC_A_2 0x004C -+#define DP_CSC_A_3 0x0050 -+#define DP_CSC_0 0x0054 -+#define DP_CSC_1 0x0058 -+ -+#define DP_COM_CONF_FG_EN (1 << 0) -+#define DP_COM_CONF_GWSEL (1 << 1) -+#define DP_COM_CONF_GWAM (1 << 2) -+#define DP_COM_CONF_GWCKE (1 << 3) -+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8) -+#define DP_COM_CONF_CSC_DEF_OFFSET 8 -+#define DP_COM_CONF_CSC_DEF_FG (3 << 8) -+#define DP_COM_CONF_CSC_DEF_BG (2 << 8) -+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8) -+ -+#define IPUV3_NUM_FLOWS 3 -+ -+struct ipu_dp_priv; -+ -+struct ipu_dp { -+ u32 flow; -+ bool in_use; -+ bool foreground; -+ enum ipu_color_space in_cs; -+}; -+ -+struct ipu_flow { -+ struct ipu_dp foreground; -+ struct ipu_dp background; -+ enum ipu_color_space out_cs; -+ void __iomem *base; -+ struct ipu_dp_priv *priv; -+}; -+ -+struct ipu_dp_priv { -+ struct ipu_soc *ipu; -+ struct device *dev; -+ void __iomem *base; -+ struct ipu_flow flow[IPUV3_NUM_FLOWS]; -+ struct mutex mutex; -+ int use_count; -+}; -+ -+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1}; -+ -+static inline struct ipu_flow *to_flow(struct ipu_dp *dp) -+{ -+ if (dp->foreground) -+ return container_of(dp, struct ipu_flow, foreground); -+ else -+ return container_of(dp, struct ipu_flow, background); -+} -+ -+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, -+ u8 alpha, bool bg_chan) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ if (bg_chan) -+ reg &= ~DP_COM_CONF_GWSEL; -+ else -+ reg |= DP_COM_CONF_GWSEL; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ if (enable) { -+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL; -+ writel(reg | ((u32) alpha << 24), -+ flow->base + DP_GRAPH_WIND_CTRL); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } else { -+ reg = readl(flow->base + DP_COM_CONF); -+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha); -+ -+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos); -+ -+static void ipu_dp_csc_init(struct ipu_flow *flow, -+ enum ipu_color_space in, -+ enum ipu_color_space out, -+ u32 place) -+{ -+ u32 reg; -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ -+ if (in == out) { -+ writel(reg, flow->base + DP_COM_CONF); -+ return; -+ } -+ -+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) { -+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0); -+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1); -+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2); -+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3); -+ writel(0x3d6 | (0x0000 << 16) | (2 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30), -+ flow->base + DP_CSC_1); -+ } else { -+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0); -+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1); -+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2); -+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3); -+ writel(0x000 | (0x3e42 << 16) | (1 << 30), -+ flow->base + DP_CSC_0); -+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30), -+ flow->base + DP_CSC_1); -+ } -+ -+ reg |= place; -+ -+ writel(reg, flow->base + DP_COM_CONF); -+} -+ -+int ipu_dp_setup_channel(struct ipu_dp *dp, -+ enum ipu_color_space in, -+ enum ipu_color_space out) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ dp->in_cs = in; -+ -+ if (!dp->foreground) -+ flow->out_cs = out; -+ -+ if (flow->foreground.in_cs == flow->background.in_cs) { -+ /* -+ * foreground and background are of same colorspace, put -+ * colorspace converter after combining unit. -+ */ -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs, -+ DP_COM_CONF_CSC_DEF_BOTH); -+ } else { -+ if (flow->foreground.in_cs == IPUV3_COLORSPACE_UNKNOWN || -+ flow->foreground.in_cs == flow->out_cs) -+ /* -+ * foreground identical to output, apply color -+ * conversion on background -+ */ -+ ipu_dp_csc_init(flow, flow->background.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG); -+ else -+ ipu_dp_csc_init(flow, flow->foreground.in_cs, -+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG); -+ } -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel); -+ -+int ipu_dp_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN); -+ -+ priv->use_count++; -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable); -+ -+int ipu_dp_enable_channel(struct ipu_dp *dp) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg; -+ -+ if (!dp->foreground) -+ return 0; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ reg |= DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ ipu_srm_dp_update(priv->ipu, true); -+ -+ mutex_unlock(&priv->mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel); -+ -+void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync) -+{ -+ struct ipu_flow *flow = to_flow(dp); -+ struct ipu_dp_priv *priv = flow->priv; -+ u32 reg, csc; -+ -+ dp->in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ -+ if (!dp->foreground) -+ return; -+ -+ mutex_lock(&priv->mutex); -+ -+ reg = readl(flow->base + DP_COM_CONF); -+ csc = reg & DP_COM_CONF_CSC_DEF_MASK; -+ reg &= ~DP_COM_CONF_CSC_DEF_MASK; -+ if (csc == DP_COM_CONF_CSC_DEF_BOTH || csc == DP_COM_CONF_CSC_DEF_BG) -+ reg |= DP_COM_CONF_CSC_DEF_BG; -+ -+ reg &= ~DP_COM_CONF_FG_EN; -+ writel(reg, flow->base + DP_COM_CONF); -+ -+ writel(0, flow->base + DP_FG_POS); -+ ipu_srm_dp_update(priv->ipu, sync); -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel); -+ -+void ipu_dp_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ -+ mutex_lock(&priv->mutex); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ mutex_unlock(&priv->mutex); -+} -+EXPORT_SYMBOL_GPL(ipu_dp_disable); -+ -+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow) -+{ -+ struct ipu_dp_priv *priv = ipu->dp_priv; -+ struct ipu_dp *dp; -+ -+ if ((flow >> 1) >= IPUV3_NUM_FLOWS) -+ return ERR_PTR(-EINVAL); -+ -+ if (flow & 1) -+ dp = &priv->flow[flow >> 1].foreground; -+ else -+ dp = &priv->flow[flow >> 1].background; -+ -+ if (dp->in_use) -+ return ERR_PTR(-EBUSY); -+ -+ dp->in_use = true; -+ -+ return dp; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_get); -+ -+void ipu_dp_put(struct ipu_dp *dp) -+{ -+ dp->in_use = false; -+} -+EXPORT_SYMBOL_GPL(ipu_dp_put); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base) -+{ -+ struct ipu_dp_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ priv->dev = dev; -+ priv->ipu = ipu; -+ -+ ipu->dp_priv = priv; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ mutex_init(&priv->mutex); -+ -+ for (i = 0; i < IPUV3_NUM_FLOWS; i++) { -+ priv->flow[i].background.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.in_cs = IPUV3_COLORSPACE_UNKNOWN; -+ priv->flow[i].foreground.foreground = true; -+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i]; -+ priv->flow[i].priv = priv; -+ } -+ -+ return 0; -+} -+ -+void ipu_dp_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-ic.c -@@ -0,0 +1,761 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2014 Mentor Graphics Inc. -+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+ -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/bitrev.h> -+#include <linux/io.h> -+#include <linux/err.h> -+#include <linux/sizes.h> -+#include "ipu-prv.h" -+ -+/* IC Register Offsets */ -+#define IC_CONF 0x0000 -+#define IC_PRP_ENC_RSC 0x0004 -+#define IC_PRP_VF_RSC 0x0008 -+#define IC_PP_RSC 0x000C -+#define IC_CMBP_1 0x0010 -+#define IC_CMBP_2 0x0014 -+#define IC_IDMAC_1 0x0018 -+#define IC_IDMAC_2 0x001C -+#define IC_IDMAC_3 0x0020 -+#define IC_IDMAC_4 0x0024 -+ -+/* IC Register Fields */ -+#define IC_CONF_PRPENC_EN (1 << 0) -+#define IC_CONF_PRPENC_CSC1 (1 << 1) -+#define IC_CONF_PRPENC_ROT_EN (1 << 2) -+#define IC_CONF_PRPVF_EN (1 << 8) -+#define IC_CONF_PRPVF_CSC1 (1 << 9) -+#define IC_CONF_PRPVF_CSC2 (1 << 10) -+#define IC_CONF_PRPVF_CMB (1 << 11) -+#define IC_CONF_PRPVF_ROT_EN (1 << 12) -+#define IC_CONF_PP_EN (1 << 16) -+#define IC_CONF_PP_CSC1 (1 << 17) -+#define IC_CONF_PP_CSC2 (1 << 18) -+#define IC_CONF_PP_CMB (1 << 19) -+#define IC_CONF_PP_ROT_EN (1 << 20) -+#define IC_CONF_IC_GLB_LOC_A (1 << 28) -+#define IC_CONF_KEY_COLOR_EN (1 << 29) -+#define IC_CONF_RWS_EN (1 << 30) -+#define IC_CONF_CSI_MEM_WR_EN (1 << 31) -+ -+#define IC_IDMAC_1_CB0_BURST_16 (1 << 0) -+#define IC_IDMAC_1_CB1_BURST_16 (1 << 1) -+#define IC_IDMAC_1_CB2_BURST_16 (1 << 2) -+#define IC_IDMAC_1_CB3_BURST_16 (1 << 3) -+#define IC_IDMAC_1_CB4_BURST_16 (1 << 4) -+#define IC_IDMAC_1_CB5_BURST_16 (1 << 5) -+#define IC_IDMAC_1_CB6_BURST_16 (1 << 6) -+#define IC_IDMAC_1_CB7_BURST_16 (1 << 7) -+#define IC_IDMAC_1_PRPENC_ROT_MASK (0x7 << 11) -+#define IC_IDMAC_1_PRPENC_ROT_OFFSET 11 -+#define IC_IDMAC_1_PRPVF_ROT_MASK (0x7 << 14) -+#define IC_IDMAC_1_PRPVF_ROT_OFFSET 14 -+#define IC_IDMAC_1_PP_ROT_MASK (0x7 << 17) -+#define IC_IDMAC_1_PP_ROT_OFFSET 17 -+#define IC_IDMAC_1_PP_FLIP_RS (1 << 22) -+#define IC_IDMAC_1_PRPVF_FLIP_RS (1 << 21) -+#define IC_IDMAC_1_PRPENC_FLIP_RS (1 << 20) -+ -+#define IC_IDMAC_2_PRPENC_HEIGHT_MASK (0x3ff << 0) -+#define IC_IDMAC_2_PRPENC_HEIGHT_OFFSET 0 -+#define IC_IDMAC_2_PRPVF_HEIGHT_MASK (0x3ff << 10) -+#define IC_IDMAC_2_PRPVF_HEIGHT_OFFSET 10 -+#define IC_IDMAC_2_PP_HEIGHT_MASK (0x3ff << 20) -+#define IC_IDMAC_2_PP_HEIGHT_OFFSET 20 -+ -+#define IC_IDMAC_3_PRPENC_WIDTH_MASK (0x3ff << 0) -+#define IC_IDMAC_3_PRPENC_WIDTH_OFFSET 0 -+#define IC_IDMAC_3_PRPVF_WIDTH_MASK (0x3ff << 10) -+#define IC_IDMAC_3_PRPVF_WIDTH_OFFSET 10 -+#define IC_IDMAC_3_PP_WIDTH_MASK (0x3ff << 20) -+#define IC_IDMAC_3_PP_WIDTH_OFFSET 20 -+ -+struct ic_task_regoffs { -+ u32 rsc; -+ u32 tpmem_csc[2]; -+}; -+ -+struct ic_task_bitfields { -+ u32 ic_conf_en; -+ u32 ic_conf_rot_en; -+ u32 ic_conf_cmb_en; -+ u32 ic_conf_csc1_en; -+ u32 ic_conf_csc2_en; -+ u32 ic_cmb_galpha_bit; -+}; -+ -+static const struct ic_task_regoffs ic_task_reg[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .rsc = IC_PRP_ENC_RSC, -+ .tpmem_csc = {0x2008, 0}, -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .rsc = IC_PRP_VF_RSC, -+ .tpmem_csc = {0x4028, 0x4040}, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .rsc = IC_PP_RSC, -+ .tpmem_csc = {0x6060, 0x6078}, -+ }, -+}; -+ -+static const struct ic_task_bitfields ic_task_bit[IC_NUM_TASKS] = { -+ [IC_TASK_ENCODER] = { -+ .ic_conf_en = IC_CONF_PRPENC_EN, -+ .ic_conf_rot_en = IC_CONF_PRPENC_ROT_EN, -+ .ic_conf_cmb_en = 0, /* NA */ -+ .ic_conf_csc1_en = IC_CONF_PRPENC_CSC1, -+ .ic_conf_csc2_en = 0, /* NA */ -+ .ic_cmb_galpha_bit = 0, /* NA */ -+ }, -+ [IC_TASK_VIEWFINDER] = { -+ .ic_conf_en = IC_CONF_PRPVF_EN, -+ .ic_conf_rot_en = IC_CONF_PRPVF_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PRPVF_CMB, -+ .ic_conf_csc1_en = IC_CONF_PRPVF_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PRPVF_CSC2, -+ .ic_cmb_galpha_bit = 0, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .ic_conf_en = IC_CONF_PP_EN, -+ .ic_conf_rot_en = IC_CONF_PP_ROT_EN, -+ .ic_conf_cmb_en = IC_CONF_PP_CMB, -+ .ic_conf_csc1_en = IC_CONF_PP_CSC1, -+ .ic_conf_csc2_en = IC_CONF_PP_CSC2, -+ .ic_cmb_galpha_bit = 8, -+ }, -+}; -+ -+struct ipu_ic_priv; -+ -+struct ipu_ic { -+ enum ipu_ic_task task; -+ const struct ic_task_regoffs *reg; -+ const struct ic_task_bitfields *bit; -+ -+ struct ipu_ic_colorspace in_cs; -+ struct ipu_ic_colorspace g_in_cs; -+ struct ipu_ic_colorspace out_cs; -+ -+ bool graphics; -+ bool rotation; -+ bool in_use; -+ -+ struct ipu_ic_priv *priv; -+}; -+ -+struct ipu_ic_priv { -+ void __iomem *base; -+ void __iomem *tpmem_base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ int use_count; -+ int irt_use_count; -+ struct ipu_ic task[IC_NUM_TASKS]; -+}; -+ -+static inline u32 ipu_ic_read(struct ipu_ic *ic, unsigned offset) -+{ -+ return readl(ic->priv->base + offset); -+} -+ -+static inline void ipu_ic_write(struct ipu_ic *ic, u32 value, unsigned offset) -+{ -+ writel(value, ic->priv->base + offset); -+} -+ -+static int init_csc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int csc_index) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 __iomem *base; -+ const u16 (*c)[3]; -+ const u16 *a; -+ u32 param; -+ -+ base = (u32 __iomem *) -+ (priv->tpmem_base + ic->reg->tpmem_csc[csc_index]); -+ -+ /* Cast to unsigned */ -+ c = (const u16 (*)[3])csc->params.coeff; -+ a = (const u16 *)csc->params.offset; -+ -+ param = ((a[0] & 0x1f) << 27) | ((c[0][0] & 0x1ff) << 18) | -+ ((c[1][1] & 0x1ff) << 9) | (c[2][2] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[0] & 0x1fe0) >> 5) | (csc->params.scale << 8) | -+ (csc->params.sat << 10); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1f) << 27) | ((c[0][1] & 0x1ff) << 18) | -+ ((c[1][0] & 0x1ff) << 9) | (c[2][0] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[1] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1f) << 27) | ((c[0][2] & 0x1ff) << 18) | -+ ((c[1][2] & 0x1ff) << 9) | (c[2][1] & 0x1ff); -+ writel(param, base++); -+ -+ param = ((a[2] & 0x1fe0) >> 5); -+ writel(param, base++); -+ -+ return 0; -+} -+ -+static int calc_resize_coeffs(struct ipu_ic *ic, -+ u32 in_size, u32 out_size, -+ u32 *resize_coeff, -+ u32 *downsize_coeff) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 temp_size, temp_downsize; -+ -+ /* -+ * Input size cannot be more than 4096, and output size cannot -+ * be more than 1024 -+ */ -+ if (in_size > 4096) { -+ dev_err(ipu->dev, "Unsupported resize (in_size > 4096)\n"); -+ return -EINVAL; -+ } -+ if (out_size > 1024) { -+ dev_err(ipu->dev, "Unsupported resize (out_size > 1024)\n"); -+ return -EINVAL; -+ } -+ -+ /* Cannot downsize more than 4:1 */ -+ if ((out_size << 2) < in_size) { -+ dev_err(ipu->dev, "Unsupported downsize\n"); -+ return -EINVAL; -+ } -+ -+ /* Compute downsizing coefficient */ -+ temp_downsize = 0; -+ temp_size = in_size; -+ while (((temp_size > 1024) || (temp_size >= out_size * 2)) && -+ (temp_downsize < 2)) { -+ temp_size >>= 1; -+ temp_downsize++; -+ } -+ *downsize_coeff = temp_downsize; -+ -+ /* -+ * compute resizing coefficient using the following equation: -+ * resize_coeff = M * (SI - 1) / (SO - 1) -+ * where M = 2^13, SI = input size, SO = output size -+ */ -+ *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); -+ if (*resize_coeff >= 16384L) { -+ dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); -+ *resize_coeff = 0x3FFF; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_task_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf |= ic->bit->ic_conf_en; -+ -+ if (ic->rotation) -+ ic_conf |= ic->bit->ic_conf_rot_en; -+ -+ if (ic->in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->graphics) { -+ ic_conf |= ic->bit->ic_conf_cmb_en; -+ ic_conf |= ic->bit->ic_conf_csc1_en; -+ -+ if (ic->g_in_cs.cs != ic->out_cs.cs) -+ ic_conf |= ic->bit->ic_conf_csc2_en; -+ } -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_enable); -+ -+void ipu_ic_task_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ u32 ic_conf; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ ic_conf &= ~(ic->bit->ic_conf_en | -+ ic->bit->ic_conf_csc1_en | -+ ic->bit->ic_conf_rot_en); -+ if (ic->bit->ic_conf_csc2_en) -+ ic_conf &= ~ic->bit->ic_conf_csc2_en; -+ if (ic->bit->ic_conf_cmb_en) -+ ic_conf &= ~ic->bit->ic_conf_cmb_en; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_disable); -+ -+int ipu_ic_task_graphics_init(struct ipu_ic *ic, -+ const struct ipu_ic_colorspace *g_in_cs, -+ bool galpha_en, u32 galpha, -+ bool colorkey_en, u32 colorkey) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_ic_csc csc2; -+ unsigned long flags; -+ u32 reg, ic_conf; -+ int ret = 0; -+ -+ if (ic->task == IC_TASK_ENCODER) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_conf = ipu_ic_read(ic, IC_CONF); -+ -+ if (!(ic_conf & ic->bit->ic_conf_csc1_en)) { -+ struct ipu_ic_csc csc1; -+ -+ ret = ipu_ic_calc_csc(&csc1, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB, -+ V4L2_YCBCR_ENC_601, -+ V4L2_QUANTIZATION_FULL_RANGE, -+ IPUV3_COLORSPACE_RGB); -+ if (ret) -+ goto unlock; -+ -+ /* need transparent CSC1 conversion */ -+ ret = init_csc(ic, &csc1, 0); -+ if (ret) -+ goto unlock; -+ } -+ -+ ic->g_in_cs = *g_in_cs; -+ csc2.in_cs = ic->g_in_cs; -+ csc2.out_cs = ic->out_cs; -+ -+ ret = __ipu_ic_calc_csc(&csc2); -+ if (ret) -+ goto unlock; -+ -+ ret = init_csc(ic, &csc2, 1); -+ if (ret) -+ goto unlock; -+ -+ if (galpha_en) { -+ ic_conf |= IC_CONF_IC_GLB_LOC_A; -+ reg = ipu_ic_read(ic, IC_CMBP_1); -+ reg &= ~(0xff << ic->bit->ic_cmb_galpha_bit); -+ reg |= (galpha << ic->bit->ic_cmb_galpha_bit); -+ ipu_ic_write(ic, reg, IC_CMBP_1); -+ } else -+ ic_conf &= ~IC_CONF_IC_GLB_LOC_A; -+ -+ if (colorkey_en) { -+ ic_conf |= IC_CONF_KEY_COLOR_EN; -+ ipu_ic_write(ic, colorkey, IC_CMBP_2); -+ } else -+ ic_conf &= ~IC_CONF_KEY_COLOR_EN; -+ -+ ipu_ic_write(ic, ic_conf, IC_CONF); -+ -+ ic->graphics = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_graphics_init); -+ -+int ipu_ic_task_init_rsc(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height, -+ u32 rsc) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ u32 downsize_coeff, resize_coeff; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!rsc) { -+ /* Setup vertical resizing */ -+ -+ ret = calc_resize_coeffs(ic, in_height, out_height, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc = (downsize_coeff << 30) | (resize_coeff << 16); -+ -+ /* Setup horizontal resizing */ -+ ret = calc_resize_coeffs(ic, in_width, out_width, -+ &resize_coeff, &downsize_coeff); -+ if (ret) -+ return ret; -+ -+ rsc |= (downsize_coeff << 14) | resize_coeff; -+ } -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ipu_ic_write(ic, rsc, ic->reg->rsc); -+ -+ /* Setup color space conversion */ -+ ic->in_cs = csc->in_cs; -+ ic->out_cs = csc->out_cs; -+ -+ ret = init_csc(ic, csc, 0); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+ -+int ipu_ic_task_init(struct ipu_ic *ic, -+ const struct ipu_ic_csc *csc, -+ int in_width, int in_height, -+ int out_width, int out_height) -+{ -+ return ipu_ic_task_init_rsc(ic, csc, -+ in_width, in_height, -+ out_width, out_height, 0); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_init); -+ -+int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel, -+ u32 width, u32 height, int burst_size, -+ enum ipu_rotate_mode rot) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; -+ u32 temp_rot = bitrev8(rot) >> 5; -+ bool need_hor_flip = false; -+ unsigned long flags; -+ int ret = 0; -+ -+ if ((burst_size != 8) && (burst_size != 16)) { -+ dev_err(ipu->dev, "Illegal burst length for IC\n"); -+ return -EINVAL; -+ } -+ -+ width--; -+ height--; -+ -+ if (temp_rot & 0x2) /* Need horizontal flip */ -+ need_hor_flip = true; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ ic_idmac_1 = ipu_ic_read(ic, IC_IDMAC_1); -+ ic_idmac_2 = ipu_ic_read(ic, IC_IDMAC_2); -+ ic_idmac_3 = ipu_ic_read(ic, IC_IDMAC_3); -+ -+ switch (channel->num) { -+ case IPUV3_CHANNEL_IC_PP_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_PP: -+ ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_ENC_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_ENC: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_IC_PRP_VF_MEM: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; -+ -+ if (need_hor_flip) -+ ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; -+ -+ ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; -+ ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; -+ -+ ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; -+ ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; -+ break; -+ case IPUV3_CHANNEL_MEM_ROT_VF: -+ ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; -+ ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; -+ break; -+ case IPUV3_CHANNEL_G_MEM_IC_PP: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; -+ break; -+ case IPUV3_CHANNEL_VDI_MEM_IC_VF: -+ if (burst_size == 16) -+ ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16; -+ else -+ ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16; -+ break; -+ default: -+ goto unlock; -+ } -+ -+ ipu_ic_write(ic, ic_idmac_1, IC_IDMAC_1); -+ ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2); -+ ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3); -+ -+ if (ipu_rot_mode_is_irt(rot)) -+ ic->rotation = true; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init); -+ -+static void ipu_irt_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (!priv->irt_use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN); -+ -+ priv->irt_use_count++; -+} -+ -+static void ipu_irt_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ -+ if (priv->irt_use_count) { -+ if (!--priv->irt_use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN); -+ } -+} -+ -+int ipu_ic_enable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_IC_EN); -+ -+ priv->use_count++; -+ -+ if (ic->rotation) -+ ipu_irt_enable(ic); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_enable); -+ -+int ipu_ic_disable(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_IC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ if (ic->rotation) -+ ipu_irt_disable(ic); -+ -+ ic->rotation = ic->graphics = false; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_disable); -+ -+struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task) -+{ -+ struct ipu_ic_priv *priv = ipu->ic_priv; -+ unsigned long flags; -+ struct ipu_ic *ic, *ret; -+ -+ if (task >= IC_NUM_TASKS) -+ return ERR_PTR(-EINVAL); -+ -+ ic = &priv->task[task]; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (ic->in_use) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ ic->in_use = true; -+ ret = ic; -+ -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_ic_get); -+ -+void ipu_ic_put(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ ic->in_use = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_put); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base) -+{ -+ struct ipu_ic_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->ic_priv = priv; -+ -+ spin_lock_init(&priv->lock); -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ priv->tpmem_base = devm_ioremap(dev, tpmem_base, SZ_64K); -+ if (!priv->tpmem_base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "IC base: 0x%08lx remapped to %p\n", base, priv->base); -+ -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ priv->task[i].task = i; -+ priv->task[i].priv = priv; -+ priv->task[i].reg = &ic_task_reg[i]; -+ priv->task[i].bit = &ic_task_bit[i]; -+ } -+ -+ return 0; -+} -+ -+void ipu_ic_exit(struct ipu_soc *ipu) -+{ -+} -+ -+void ipu_ic_dump(struct ipu_ic *ic) -+{ -+ struct ipu_ic_priv *priv = ic->priv; -+ struct ipu_soc *ipu = priv->ipu; -+ -+ dev_dbg(ipu->dev, "IC_CONF = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CONF)); -+ dev_dbg(ipu->dev, "IC_PRP_ENC_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_ENC_RSC)); -+ dev_dbg(ipu->dev, "IC_PRP_VF_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PRP_VF_RSC)); -+ dev_dbg(ipu->dev, "IC_PP_RSC = \t0x%08X\n", -+ ipu_ic_read(ic, IC_PP_RSC)); -+ dev_dbg(ipu->dev, "IC_CMBP_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_1)); -+ dev_dbg(ipu->dev, "IC_CMBP_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_CMBP_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_1 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_1)); -+ dev_dbg(ipu->dev, "IC_IDMAC_2 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_2)); -+ dev_dbg(ipu->dev, "IC_IDMAC_3 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_3)); -+ dev_dbg(ipu->dev, "IC_IDMAC_4 = \t0x%08X\n", -+ ipu_ic_read(ic, IC_IDMAC_4)); -+} -+EXPORT_SYMBOL_GPL(ipu_ic_dump); ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-image-convert.c -@@ -0,0 +1,2475 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * -+ * Queued image conversion support, with tiling and rotation. -+ */ -+ -+#include <linux/interrupt.h> -+#include <linux/dma-mapping.h> -+#include <video/imx-ipu-image-convert.h> -+#include "ipu-prv.h" -+ -+/* -+ * The IC Resizer has a restriction that the output frame from the -+ * resizer must be 1024 or less in both width (pixels) and height -+ * (lines). -+ * -+ * The image converter attempts to split up a conversion when -+ * the desired output (converted) frame resolution exceeds the -+ * IC resizer limit of 1024 in either dimension. -+ * -+ * If either dimension of the output frame exceeds the limit, the -+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum -+ * of 4*4 or 16 tiles. A conversion is then carried out for each -+ * tile (but taking care to pass the full frame stride length to -+ * the DMA channel's parameter memory!). IDMA double-buffering is used -+ * to convert each tile back-to-back when possible (see note below -+ * when double_buffering boolean is set). -+ * -+ * Note that the input frame must be split up into the same number -+ * of tiles as the output frame: -+ * -+ * +---------+-----+ -+ * +-----+---+ | A | B | -+ * | A | B | | | | -+ * +-----+---+ --> +---------+-----+ -+ * | C | D | | C | D | -+ * +-----+---+ | | | -+ * +---------+-----+ -+ * -+ * Clockwise 90° rotations are handled by first rescaling into a -+ * reusable temporary tile buffer and then rotating with the 8x8 -+ * block rotator, writing to the correct destination: -+ * -+ * +-----+-----+ -+ * | | | -+ * +-----+---+ +---------+ | C | A | -+ * | A | B | | A,B, | | | | | -+ * +-----+---+ --> | C,D | | --> | | | -+ * | C | D | +---------+ +-----+-----+ -+ * +-----+---+ | D | B | -+ * | | | -+ * +-----+-----+ -+ * -+ * If the 8x8 block rotator is used, horizontal or vertical flipping -+ * is done during the rotation step, otherwise flipping is done -+ * during the scaling step. -+ * With rotation or flipping, tile order changes between input and -+ * output image. Tiles are numbered row major from top left to bottom -+ * right for both input and output image. -+ */ -+ -+#define MAX_STRIPES_W 4 -+#define MAX_STRIPES_H 4 -+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H) -+ -+#define MIN_W 16 -+#define MIN_H 8 -+#define MAX_W 4096 -+#define MAX_H 4096 -+ -+enum ipu_image_convert_type { -+ IMAGE_CONVERT_IN = 0, -+ IMAGE_CONVERT_OUT, -+}; -+ -+struct ipu_image_convert_dma_buf { -+ void *virt; -+ dma_addr_t phys; -+ unsigned long len; -+}; -+ -+struct ipu_image_convert_dma_chan { -+ int in; -+ int out; -+ int rot_in; -+ int rot_out; -+ int vdi_in_p; -+ int vdi_in; -+ int vdi_in_n; -+}; -+ -+/* dimensions of one tile */ -+struct ipu_image_tile { -+ u32 width; -+ u32 height; -+ u32 left; -+ u32 top; -+ /* size and strides are in bytes */ -+ u32 size; -+ u32 stride; -+ u32 rot_stride; -+ /* start Y or packed offset of this tile */ -+ u32 offset; -+ /* offset from start to tile in U plane, for planar formats */ -+ u32 u_off; -+ /* offset from start to tile in V plane, for planar formats */ -+ u32 v_off; -+}; -+ -+struct ipu_image_convert_image { -+ struct ipu_image base; -+ enum ipu_image_convert_type type; -+ -+ const struct ipu_image_pixfmt *fmt; -+ unsigned int stride; -+ -+ /* # of rows (horizontal stripes) if dest height is > 1024 */ -+ unsigned int num_rows; -+ /* # of columns (vertical stripes) if dest width is > 1024 */ -+ unsigned int num_cols; -+ -+ struct ipu_image_tile tile[MAX_TILES]; -+}; -+ -+struct ipu_image_pixfmt { -+ u32 fourcc; /* V4L2 fourcc */ -+ int bpp; /* total bpp */ -+ int uv_width_dec; /* decimation in width for U/V planes */ -+ int uv_height_dec; /* decimation in height for U/V planes */ -+ bool planar; /* planar format */ -+ bool uv_swapped; /* U and V planes are swapped */ -+ bool uv_packed; /* partial planar (U and V in same plane) */ -+}; -+ -+struct ipu_image_convert_ctx; -+struct ipu_image_convert_chan; -+struct ipu_image_convert_priv; -+ -+struct ipu_image_convert_ctx { -+ struct ipu_image_convert_chan *chan; -+ -+ ipu_image_convert_cb_t complete; -+ void *complete_context; -+ -+ /* Source/destination image data and rotation mode */ -+ struct ipu_image_convert_image in; -+ struct ipu_image_convert_image out; -+ struct ipu_ic_csc csc; -+ enum ipu_rotate_mode rot_mode; -+ u32 downsize_coeff_h; -+ u32 downsize_coeff_v; -+ u32 image_resize_coeff_h; -+ u32 image_resize_coeff_v; -+ u32 resize_coeffs_h[MAX_STRIPES_W]; -+ u32 resize_coeffs_v[MAX_STRIPES_H]; -+ -+ /* intermediate buffer for rotation */ -+ struct ipu_image_convert_dma_buf rot_intermediate[2]; -+ -+ /* current buffer number for double buffering */ -+ int cur_buf_num; -+ -+ bool aborting; -+ struct completion aborted; -+ -+ /* can we use double-buffering for this conversion operation? */ -+ bool double_buffering; -+ /* num_rows * num_cols */ -+ unsigned int num_tiles; -+ /* next tile to process */ -+ unsigned int next_tile; -+ /* where to place converted tile in dest image */ -+ unsigned int out_tile_map[MAX_TILES]; -+ -+ struct list_head list; -+}; -+ -+struct ipu_image_convert_chan { -+ struct ipu_image_convert_priv *priv; -+ -+ enum ipu_ic_task ic_task; -+ const struct ipu_image_convert_dma_chan *dma_ch; -+ -+ struct ipu_ic *ic; -+ struct ipuv3_channel *in_chan; -+ struct ipuv3_channel *out_chan; -+ struct ipuv3_channel *rotation_in_chan; -+ struct ipuv3_channel *rotation_out_chan; -+ -+ /* the IPU end-of-frame irqs */ -+ int out_eof_irq; -+ int rot_out_eof_irq; -+ -+ spinlock_t irqlock; -+ -+ /* list of convert contexts */ -+ struct list_head ctx_list; -+ /* queue of conversion runs */ -+ struct list_head pending_q; -+ /* queue of completed runs */ -+ struct list_head done_q; -+ -+ /* the current conversion run */ -+ struct ipu_image_convert_run *current_run; -+}; -+ -+struct ipu_image_convert_priv { -+ struct ipu_image_convert_chan chan[IC_NUM_TASKS]; -+ struct ipu_soc *ipu; -+}; -+ -+static const struct ipu_image_convert_dma_chan -+image_convert_dma_chan[IC_NUM_TASKS] = { -+ [IC_TASK_VIEWFINDER] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PRP_VF, -+ .out = IPUV3_CHANNEL_IC_PRP_VF_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_VF, -+ .rot_out = IPUV3_CHANNEL_ROT_VF_MEM, -+ .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV, -+ .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR, -+ .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT, -+ }, -+ [IC_TASK_POST_PROCESSOR] = { -+ .in = IPUV3_CHANNEL_MEM_IC_PP, -+ .out = IPUV3_CHANNEL_IC_PP_MEM, -+ .rot_in = IPUV3_CHANNEL_MEM_ROT_PP, -+ .rot_out = IPUV3_CHANNEL_ROT_PP_MEM, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt image_convert_formats[] = { -+ { -+ .fourcc = V4L2_PIX_FMT_RGB565, -+ .bpp = 16, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .bpp = 24, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XRGB32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_XBGR32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_BGRX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_RGBX32, -+ .bpp = 32, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .bpp = 16, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_swapped = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .bpp = 12, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 2, -+ .uv_packed = true, -+ }, { -+ .fourcc = V4L2_PIX_FMT_YUV422P, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ }, { -+ .fourcc = V4L2_PIX_FMT_NV16, -+ .bpp = 16, -+ .planar = true, -+ .uv_width_dec = 2, -+ .uv_height_dec = 1, -+ .uv_packed = true, -+ }, -+}; -+ -+static const struct ipu_image_pixfmt *get_format(u32 fourcc) -+{ -+ const struct ipu_image_pixfmt *ret = NULL; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) { -+ if (image_convert_formats[i].fourcc == fourcc) { -+ ret = &image_convert_formats[i]; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+static void dump_format(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s format: %dx%d (%dx%d tiles), %c%c%c%c\n", -+ chan->ic_task, ctx, -+ ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input", -+ ic_image->base.pix.width, ic_image->base.pix.height, -+ ic_image->num_cols, ic_image->num_rows, -+ ic_image->fmt->fourcc & 0xff, -+ (ic_image->fmt->fourcc >> 8) & 0xff, -+ (ic_image->fmt->fourcc >> 16) & 0xff, -+ (ic_image->fmt->fourcc >> 24) & 0xff); -+} -+ -+int ipu_image_convert_enum_format(int index, u32 *fourcc) -+{ -+ const struct ipu_image_pixfmt *fmt; -+ -+ if (index >= (int)ARRAY_SIZE(image_convert_formats)) -+ return -EINVAL; -+ -+ /* Format found */ -+ fmt = &image_convert_formats[index]; -+ *fourcc = fmt->fourcc; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format); -+ -+static void free_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf) -+{ -+ if (buf->virt) -+ dma_free_coherent(priv->ipu->dev, -+ buf->len, buf->virt, buf->phys); -+ buf->virt = NULL; -+ buf->phys = 0; -+} -+ -+static int alloc_dma_buf(struct ipu_image_convert_priv *priv, -+ struct ipu_image_convert_dma_buf *buf, -+ int size) -+{ -+ buf->len = PAGE_ALIGN(size); -+ buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys, -+ GFP_DMA | GFP_KERNEL); -+ if (!buf->virt) { -+ dev_err(priv->ipu->dev, "failed to alloc dma buffer\n"); -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+static inline int num_stripes(int dim) -+{ -+ return (dim - 1) / 1024 + 1; -+} -+ -+/* -+ * Calculate downsizing coefficients, which are the same for all tiles, -+ * and initial bilinear resizing coefficients, which are used to find the -+ * best seam positions. -+ * Also determine the number of tiles necessary to guarantee that no tile -+ * is larger than 1024 pixels in either dimension at the output and between -+ * IC downsizing and main processing sections. -+ */ -+static int calc_image_resize_coefficients(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image *in, -+ struct ipu_image *out) -+{ -+ u32 downsized_width = in->rect.width; -+ u32 downsized_height = in->rect.height; -+ u32 downsize_coeff_v = 0; -+ u32 downsize_coeff_h = 0; -+ u32 resized_width = out->rect.width; -+ u32 resized_height = out->rect.height; -+ u32 resize_coeff_h; -+ u32 resize_coeff_v; -+ u32 cols; -+ u32 rows; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ resized_width = out->rect.height; -+ resized_height = out->rect.width; -+ } -+ -+ /* Do not let invalid input lead to an endless loop below */ -+ if (WARN_ON(resized_width == 0 || resized_height == 0)) -+ return -EINVAL; -+ -+ while (downsized_width >= resized_width * 2) { -+ downsized_width >>= 1; -+ downsize_coeff_h++; -+ } -+ -+ while (downsized_height >= resized_height * 2) { -+ downsized_height >>= 1; -+ downsize_coeff_v++; -+ } -+ -+ /* -+ * Calculate the bilinear resizing coefficients that could be used if -+ * we were converting with a single tile. The bottom right output pixel -+ * should sample as close as possible to the bottom right input pixel -+ * out of the decimator, but not overshoot it: -+ */ -+ resize_coeff_h = 8192 * (downsized_width - 1) / (resized_width - 1); -+ resize_coeff_v = 8192 * (downsized_height - 1) / (resized_height - 1); -+ -+ /* -+ * Both the output of the IC downsizing section before being passed to -+ * the IC main processing section and the final output of the IC main -+ * processing section must be <= 1024 pixels in both dimensions. -+ */ -+ cols = num_stripes(max_t(u32, downsized_width, resized_width)); -+ rows = num_stripes(max_t(u32, downsized_height, resized_height)); -+ -+ dev_dbg(ctx->chan->priv->ipu->dev, -+ "%s: hscale: >>%u, *8192/%u vscale: >>%u, *8192/%u, %ux%u tiles\n", -+ __func__, downsize_coeff_h, resize_coeff_h, downsize_coeff_v, -+ resize_coeff_v, cols, rows); -+ -+ if (downsize_coeff_h > 2 || downsize_coeff_v > 2 || -+ resize_coeff_h > 0x3fff || resize_coeff_v > 0x3fff) -+ return -EINVAL; -+ -+ ctx->downsize_coeff_h = downsize_coeff_h; -+ ctx->downsize_coeff_v = downsize_coeff_v; -+ ctx->image_resize_coeff_h = resize_coeff_h; -+ ctx->image_resize_coeff_v = resize_coeff_v; -+ ctx->in.num_cols = cols; -+ ctx->in.num_rows = rows; -+ -+ return 0; -+} -+ -+#define round_closest(x, y) round_down((x) + (y)/2, (y)) -+ -+/* -+ * Find the best aligned seam position for the given column / row index. -+ * Rotation and image offsets are out of scope. -+ * -+ * @index: column / row index, used to calculate valid interval -+ * @in_edge: input right / bottom edge -+ * @out_edge: output right / bottom edge -+ * @in_align: input alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format -+ * @out_align: output alignment, either horizontal 8-byte line start address -+ * alignment, or pixel alignment due to image format or rotator -+ * block size -+ * @in_burst: horizontal input burst size in case of horizontal flip -+ * @out_burst: horizontal output burst size or rotator block size -+ * @downsize_coeff: downsizing section coefficient -+ * @resize_coeff: main processing section resizing coefficient -+ * @_in_seam: aligned input seam position return value -+ * @_out_seam: aligned output seam position return value -+ */ -+static void find_best_seam(struct ipu_image_convert_ctx *ctx, -+ unsigned int index, -+ unsigned int in_edge, -+ unsigned int out_edge, -+ unsigned int in_align, -+ unsigned int out_align, -+ unsigned int in_burst, -+ unsigned int out_burst, -+ unsigned int downsize_coeff, -+ unsigned int resize_coeff, -+ u32 *_in_seam, -+ u32 *_out_seam) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int out_pos; -+ /* Input / output seam position candidates */ -+ unsigned int out_seam = 0; -+ unsigned int in_seam = 0; -+ unsigned int min_diff = UINT_MAX; -+ unsigned int out_start; -+ unsigned int out_end; -+ unsigned int in_start; -+ unsigned int in_end; -+ -+ /* Start within 1024 pixels of the right / bottom edge */ -+ out_start = max_t(int, index * out_align, out_edge - 1024); -+ /* End before having to add more columns to the left / rows above */ -+ out_end = min_t(unsigned int, out_edge, index * 1024 + 1); -+ -+ /* -+ * Limit input seam position to make sure that the downsized input tile -+ * to the right or bottom does not exceed 1024 pixels. -+ */ -+ in_start = max_t(int, index * in_align, -+ in_edge - (1024 << downsize_coeff)); -+ in_end = min_t(unsigned int, in_edge, -+ index * (1024 << downsize_coeff) + 1); -+ -+ /* -+ * Output tiles must start at a multiple of 8 bytes horizontally and -+ * possibly at an even line horizontally depending on the pixel format. -+ * Only consider output aligned positions for the seam. -+ */ -+ out_start = round_up(out_start, out_align); -+ for (out_pos = out_start; out_pos < out_end; out_pos += out_align) { -+ unsigned int in_pos; -+ unsigned int in_pos_aligned; -+ unsigned int in_pos_rounded; -+ unsigned int abs_diff; -+ -+ /* -+ * Tiles in the right row / bottom column may not be allowed to -+ * overshoot horizontally / vertically. out_burst may be the -+ * actual DMA burst size, or the rotator block size. -+ */ -+ if ((out_burst > 1) && (out_edge - out_pos) % out_burst) -+ continue; -+ -+ /* -+ * Input sample position, corresponding to out_pos, 19.13 fixed -+ * point. -+ */ -+ in_pos = (out_pos * resize_coeff) << downsize_coeff; -+ /* -+ * The closest input sample position that we could actually -+ * start the input tile at, 19.13 fixed point. -+ */ -+ in_pos_aligned = round_closest(in_pos, 8192U * in_align); -+ /* Convert 19.13 fixed point to integer */ -+ in_pos_rounded = in_pos_aligned / 8192U; -+ -+ if (in_pos_rounded < in_start) -+ continue; -+ if (in_pos_rounded >= in_end) -+ break; -+ -+ if ((in_burst > 1) && -+ (in_edge - in_pos_rounded) % in_burst) -+ continue; -+ -+ if (in_pos < in_pos_aligned) -+ abs_diff = in_pos_aligned - in_pos; -+ else -+ abs_diff = in_pos - in_pos_aligned; -+ -+ if (abs_diff < min_diff) { -+ in_seam = in_pos_rounded; -+ out_seam = out_pos; -+ min_diff = abs_diff; -+ } -+ } -+ -+ *_out_seam = out_seam; -+ *_in_seam = in_seam; -+ -+ dev_dbg(dev, "%s: out_seam %u(%u) in [%u, %u], in_seam %u(%u) in [%u, %u] diff %u.%03u\n", -+ __func__, out_seam, out_align, out_start, out_end, -+ in_seam, in_align, in_start, in_end, min_diff / 8192, -+ DIV_ROUND_CLOSEST(min_diff % 8192 * 1000, 8192)); -+} -+ -+/* -+ * Tile left edges are required to be aligned to multiples of 8 bytes -+ * by the IDMAC. -+ */ -+static inline u32 tile_left_align(const struct ipu_image_pixfmt *fmt) -+{ -+ if (fmt->planar) -+ return fmt->uv_packed ? 8 : 8 * fmt->uv_width_dec; -+ else -+ return fmt->bpp == 32 ? 2 : fmt->bpp == 16 ? 4 : 8; -+} -+ -+/* -+ * Tile top edge alignment is only limited by chroma subsampling. -+ */ -+static inline u32 tile_top_align(const struct ipu_image_pixfmt *fmt) -+{ -+ return fmt->uv_height_dec > 1 ? 2 : 1; -+} -+ -+static inline u32 tile_width_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN) { -+ /* -+ * The IC burst reads 8 pixels at a time. Reading beyond the -+ * end of the line is usually acceptable. Those pixels are -+ * ignored, unless the IC has to write the scaled line in -+ * reverse. -+ */ -+ return (!ipu_rot_mode_is_irt(rot_mode) && -+ (rot_mode & IPU_ROT_BIT_HFLIP)) ? 8 : 2; -+ } -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (ipu_rot_mode_is_irt(rot_mode) && -+ fmt->planar && !fmt->uv_packed) ? -+ 8 * fmt->uv_width_dec : 8; -+} -+ -+static inline u32 tile_height_align(enum ipu_image_convert_type type, -+ const struct ipu_image_pixfmt *fmt, -+ enum ipu_rotate_mode rot_mode) -+{ -+ if (type == IMAGE_CONVERT_IN || !ipu_rot_mode_is_irt(rot_mode)) -+ return 2; -+ -+ /* -+ * Align to 16x16 pixel blocks for planar 4:2:0 chroma subsampled -+ * formats to guarantee 8-byte aligned line start addresses in the -+ * chroma planes when IRT is used. Align to 8x8 pixel IRT block size -+ * for all other formats. -+ */ -+ return (fmt->planar && !fmt->uv_packed) ? 8 * fmt->uv_width_dec : 8; -+} -+ -+/* -+ * Fill in left position and width and for all tiles in an input column, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a row, and output tile top position and height are set. -+ */ -+static void fill_tile_column(struct ipu_image_convert_ctx *ctx, -+ unsigned int col, -+ struct ipu_image_convert_image *in, -+ unsigned int in_left, unsigned int in_width, -+ struct ipu_image_convert_image *out, -+ unsigned int out_left, unsigned int out_width) -+{ -+ unsigned int row, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (row = 0; row < in->num_rows; row++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->left = in_left; -+ in_tile->width = in_width; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->top = out_left; -+ out_tile->height = out_width; -+ } else { -+ out_tile->left = out_left; -+ out_tile->width = out_width; -+ } -+ } -+} -+ -+/* -+ * Fill in top position and height and for all tiles in an input row, and -+ * for all corresponding output tiles. If the 90° rotator is used, the output -+ * tiles are in a column, and output tile left position and width are set. -+ */ -+static void fill_tile_row(struct ipu_image_convert_ctx *ctx, unsigned int row, -+ struct ipu_image_convert_image *in, -+ unsigned int in_top, unsigned int in_height, -+ struct ipu_image_convert_image *out, -+ unsigned int out_top, unsigned int out_height) -+{ -+ unsigned int col, tile_idx; -+ struct ipu_image_tile *in_tile, *out_tile; -+ -+ for (col = 0; col < in->num_cols; col++) { -+ tile_idx = in->num_cols * row + col; -+ in_tile = &in->tile[tile_idx]; -+ out_tile = &out->tile[ctx->out_tile_map[tile_idx]]; -+ -+ in_tile->top = in_top; -+ in_tile->height = in_height; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ out_tile->left = out_top; -+ out_tile->width = out_height; -+ } else { -+ out_tile->top = out_top; -+ out_tile->height = out_height; -+ } -+ } -+} -+ -+/* -+ * Find the best horizontal and vertical seam positions to split into tiles. -+ * Minimize the fractional part of the input sampling position for the -+ * top / left pixels of each tile. -+ */ -+static void find_seams(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *in, -+ struct ipu_image_convert_image *out) -+{ -+ struct device *dev = ctx->chan->priv->ipu->dev; -+ unsigned int resized_width = out->base.rect.width; -+ unsigned int resized_height = out->base.rect.height; -+ unsigned int col; -+ unsigned int row; -+ unsigned int in_left_align = tile_left_align(in->fmt); -+ unsigned int in_top_align = tile_top_align(in->fmt); -+ unsigned int out_left_align = tile_left_align(out->fmt); -+ unsigned int out_top_align = tile_top_align(out->fmt); -+ unsigned int out_width_align = tile_width_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int out_height_align = tile_height_align(out->type, out->fmt, -+ ctx->rot_mode); -+ unsigned int in_right = in->base.rect.width; -+ unsigned int in_bottom = in->base.rect.height; -+ unsigned int out_right = out->base.rect.width; -+ unsigned int out_bottom = out->base.rect.height; -+ unsigned int flipped_out_left; -+ unsigned int flipped_out_top; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* Switch width/height and align top left to IRT block size */ -+ resized_width = out->base.rect.height; -+ resized_height = out->base.rect.width; -+ out_left_align = out_height_align; -+ out_top_align = out_width_align; -+ out_width_align = out_left_align; -+ out_height_align = out_top_align; -+ out_right = out->base.rect.height; -+ out_bottom = out->base.rect.width; -+ } -+ -+ for (col = in->num_cols - 1; col > 0; col--) { -+ bool allow_in_overshoot = ipu_rot_mode_is_irt(ctx->rot_mode) || -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ bool allow_out_overshoot = (col < in->num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ unsigned int in_left; -+ unsigned int out_left; -+ -+ /* -+ * Align input width to burst length if the scaling step flips -+ * horizontally. -+ */ -+ -+ find_best_seam(ctx, col, -+ in_right, out_right, -+ in_left_align, out_left_align, -+ allow_in_overshoot ? 1 : 8 /* burst length */, -+ allow_out_overshoot ? 1 : out_width_align, -+ ctx->downsize_coeff_h, ctx->image_resize_coeff_h, -+ &in_left, &out_left); -+ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ flipped_out_left = resized_width - out_right; -+ else -+ flipped_out_left = out_left; -+ -+ fill_tile_column(ctx, col, in, in_left, in_right - in_left, -+ out, flipped_out_left, out_right - out_left); -+ -+ dev_dbg(dev, "%s: col %u: %u, %u -> %u, %u\n", __func__, col, -+ in_left, in_right - in_left, -+ flipped_out_left, out_right - out_left); -+ -+ in_right = in_left; -+ out_right = out_left; -+ } -+ -+ flipped_out_left = (ctx->rot_mode & IPU_ROT_BIT_HFLIP) ? -+ resized_width - out_right : 0; -+ -+ fill_tile_column(ctx, 0, in, 0, in_right, -+ out, flipped_out_left, out_right); -+ -+ dev_dbg(dev, "%s: col 0: 0, %u -> %u, %u\n", __func__, -+ in_right, flipped_out_left, out_right); -+ -+ for (row = in->num_rows - 1; row > 0; row--) { -+ bool allow_overshoot = row < in->num_rows - 1; -+ unsigned int in_top; -+ unsigned int out_top; -+ -+ find_best_seam(ctx, row, -+ in_bottom, out_bottom, -+ in_top_align, out_top_align, -+ 1, allow_overshoot ? 1 : out_height_align, -+ ctx->downsize_coeff_v, ctx->image_resize_coeff_v, -+ &in_top, &out_top); -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = out_top; -+ -+ fill_tile_row(ctx, row, in, in_top, in_bottom - in_top, -+ out, flipped_out_top, out_bottom - out_top); -+ -+ dev_dbg(dev, "%s: row %u: %u, %u -> %u, %u\n", __func__, row, -+ in_top, in_bottom - in_top, -+ flipped_out_top, out_bottom - out_top); -+ -+ in_bottom = in_top; -+ out_bottom = out_top; -+ } -+ -+ if ((ctx->rot_mode & IPU_ROT_BIT_VFLIP) ^ -+ ipu_rot_mode_is_irt(ctx->rot_mode)) -+ flipped_out_top = resized_height - out_bottom; -+ else -+ flipped_out_top = 0; -+ -+ fill_tile_row(ctx, 0, in, 0, in_bottom, -+ out, flipped_out_top, out_bottom); -+ -+ dev_dbg(dev, "%s: row 0: 0, %u -> %u, %u\n", __func__, -+ in_bottom, flipped_out_top, out_bottom); -+} -+ -+static int calc_tile_dimensions(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned int max_width = 1024; -+ unsigned int max_height = 1024; -+ unsigned int i; -+ -+ if (image->type == IMAGE_CONVERT_IN) { -+ /* Up to 4096x4096 input tile size */ -+ max_width <<= ctx->downsize_coeff_h; -+ max_height <<= ctx->downsize_coeff_v; -+ } -+ -+ for (i = 0; i < ctx->num_tiles; i++) { -+ struct ipu_image_tile *tile; -+ const unsigned int row = i / image->num_cols; -+ const unsigned int col = i % image->num_cols; -+ -+ if (image->type == IMAGE_CONVERT_OUT) -+ tile = &image->tile[ctx->out_tile_map[i]]; -+ else -+ tile = &image->tile[i]; -+ -+ tile->size = ((tile->height * image->fmt->bpp) >> 3) * -+ tile->width; -+ -+ if (image->fmt->planar) { -+ tile->stride = tile->width; -+ tile->rot_stride = tile->height; -+ } else { -+ tile->stride = -+ (image->fmt->bpp * tile->width) >> 3; -+ tile->rot_stride = -+ (image->fmt->bpp * tile->height) >> 3; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%u,%u]: %ux%u@%u,%u\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? "Input" : "Output", -+ row, col, -+ tile->width, tile->height, tile->left, tile->top); -+ -+ if (!tile->width || tile->width > max_width || -+ !tile->height || tile->height > max_height) { -+ dev_err(priv->ipu->dev, "invalid %s tile size: %ux%u\n", -+ image->type == IMAGE_CONVERT_IN ? "input" : -+ "output", tile->width, tile->height); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * Use the rotation transformation to find the tile coordinates -+ * (row, col) of a tile in the destination frame that corresponds -+ * to the given tile coordinates of a source frame. The destination -+ * coordinate is then converted to a tile index. -+ */ -+static int transform_tile_index(struct ipu_image_convert_ctx *ctx, -+ int src_row, int src_col) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ int dst_row, dst_col; -+ -+ /* with no rotation it's a 1:1 mapping */ -+ if (ctx->rot_mode == IPU_ROTATE_NONE) -+ return src_row * s_image->num_cols + src_col; -+ -+ /* -+ * before doing the transform, first we have to translate -+ * source row,col for an origin in the center of s_image -+ */ -+ src_row = src_row * 2 - (s_image->num_rows - 1); -+ src_col = src_col * 2 - (s_image->num_cols - 1); -+ -+ /* do the rotation transform */ -+ if (ctx->rot_mode & IPU_ROT_BIT_90) { -+ dst_col = -src_row; -+ dst_row = src_col; -+ } else { -+ dst_col = src_col; -+ dst_row = src_row; -+ } -+ -+ /* apply flip */ -+ if (ctx->rot_mode & IPU_ROT_BIT_HFLIP) -+ dst_col = -dst_col; -+ if (ctx->rot_mode & IPU_ROT_BIT_VFLIP) -+ dst_row = -dst_row; -+ -+ dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n", -+ chan->ic_task, ctx, src_col, src_row, dst_col, dst_row); -+ -+ /* -+ * finally translate dest row,col using an origin in upper -+ * left of d_image -+ */ -+ dst_row += d_image->num_rows - 1; -+ dst_col += d_image->num_cols - 1; -+ dst_row /= 2; -+ dst_col /= 2; -+ -+ return dst_row * d_image->num_cols + dst_col; -+} -+ -+/* -+ * Fill the out_tile_map[] with transformed destination tile indeces. -+ */ -+static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ unsigned int row, col, tile = 0; -+ -+ for (row = 0; row < s_image->num_rows; row++) { -+ for (col = 0; col < s_image->num_cols; col++) { -+ ctx->out_tile_map[tile] = -+ transform_tile_index(ctx, row, col); -+ tile++; -+ } -+ } -+} -+ -+static int calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 H, top, y_stride, uv_stride; -+ u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp; -+ u32 y_row_off, y_col_off, y_off; -+ u32 y_size, uv_size; -+ -+ /* setup some convenience vars */ -+ H = image->base.pix.height; -+ -+ y_stride = image->stride; -+ uv_stride = y_stride / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_stride *= 2; -+ -+ y_size = H * y_stride; -+ uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec); -+ -+ for (row = 0; row < image->num_rows; row++) { -+ top = image->tile[tile].top; -+ y_row_off = top * y_stride; -+ uv_row_off = (top * uv_stride) / fmt->uv_height_dec; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ y_col_off = image->tile[tile].left; -+ uv_col_off = y_col_off / fmt->uv_width_dec; -+ if (fmt->uv_packed) -+ uv_col_off *= 2; -+ -+ y_off = y_row_off + y_col_off; -+ uv_off = uv_row_off + uv_col_off; -+ -+ u_off = y_size - y_off + uv_off; -+ v_off = (fmt->uv_packed) ? 0 : u_off + uv_size; -+ if (fmt->uv_swapped) { -+ tmp = u_off; -+ u_off = v_off; -+ v_off = tmp; -+ } -+ -+ image->tile[tile].offset = y_off; -+ image->tile[tile].u_off = u_off; -+ image->tile[tile++].v_off = v_off; -+ -+ if ((y_off & 0x7) || (u_off & 0x7) || (v_off & 0x7)) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "y_off %08x, u_off %08x, v_off %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ y_off, u_off, v_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ const struct ipu_image_pixfmt *fmt = image->fmt; -+ unsigned int row, col, tile = 0; -+ u32 bpp, stride, offset; -+ u32 row_off, col_off; -+ -+ /* setup some convenience vars */ -+ stride = image->stride; -+ bpp = fmt->bpp; -+ -+ for (row = 0; row < image->num_rows; row++) { -+ row_off = image->tile[tile].top * stride; -+ -+ for (col = 0; col < image->num_cols; col++) { -+ col_off = (image->tile[tile].left * bpp) >> 3; -+ -+ offset = row_off + col_off; -+ -+ image->tile[tile].offset = offset; -+ image->tile[tile].u_off = 0; -+ image->tile[tile++].v_off = 0; -+ -+ if (offset & 0x7) { -+ dev_err(priv->ipu->dev, -+ "task %u: ctx %p: %s@[%d,%d]: " -+ "phys %08x\n", -+ chan->ic_task, ctx, -+ image->type == IMAGE_CONVERT_IN ? -+ "Input" : "Output", row, col, -+ row_off + col_off); -+ return -EINVAL; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static int calc_tile_offsets(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *image) -+{ -+ if (image->fmt->planar) -+ return calc_tile_offsets_planar(ctx, image); -+ -+ return calc_tile_offsets_packed(ctx, image); -+} -+ -+/* -+ * Calculate the resizing ratio for the IC main processing section given input -+ * size, fixed downsizing coefficient, and output size. -+ * Either round to closest for the next tile's first pixel to minimize seams -+ * and distortion (for all but right column / bottom row), or round down to -+ * avoid sampling beyond the edges of the input image for this tile's last -+ * pixel. -+ * Returns the resizing coefficient, resizing ratio is 8192.0 / resize_coeff. -+ */ -+static u32 calc_resize_coeff(u32 input_size, u32 downsize_coeff, -+ u32 output_size, bool allow_overshoot) -+{ -+ u32 downsized = input_size >> downsize_coeff; -+ -+ if (allow_overshoot) -+ return DIV_ROUND_CLOSEST(8192 * downsized, output_size); -+ else -+ return 8192 * (downsized - 1) / (output_size - 1); -+} -+ -+/* -+ * Slightly modify resize coefficients per tile to hide the bilinear -+ * interpolator reset at tile borders, shifting the right / bottom edge -+ * by up to a half input pixel. This removes noticeable seams between -+ * tiles at higher upscaling factors. -+ */ -+static void calc_tile_resize_coefficients(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_tile *in_tile, *out_tile; -+ unsigned int col, row, tile_idx; -+ unsigned int last_output; -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ bool closest = (col < ctx->in.num_cols - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_HFLIP); -+ u32 resized_width; -+ u32 resize_coeff_h; -+ u32 in_width; -+ -+ tile_idx = col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_width = out_tile->height; -+ else -+ resized_width = out_tile->width; -+ -+ resize_coeff_h = calc_resize_coeff(in_tile->width, -+ ctx->downsize_coeff_h, -+ resized_width, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: column %u hscale: *8192/%u\n", -+ __func__, col, resize_coeff_h); -+ -+ /* -+ * With the horizontal scaling factor known, round up resized -+ * width (output width or height) to burst size. -+ */ -+ resized_width = round_up(resized_width, 8); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized width and scaling coefficients. Round up to -+ * burst size. -+ */ -+ last_output = resized_width - 1; -+ if (closest && ((last_output * resize_coeff_h) % 8192)) -+ last_output++; -+ in_width = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_h, 8192) + 1) -+ << ctx->downsize_coeff_h, 8); -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->height = resized_width; -+ else -+ out_tile->width = resized_width; -+ -+ in_tile->width = in_width; -+ } -+ -+ ctx->resize_coeffs_h[col] = resize_coeff_h; -+ } -+ -+ for (row = 0; row < ctx->in.num_rows; row++) { -+ bool closest = (row < ctx->in.num_rows - 1) && -+ !(ctx->rot_mode & IPU_ROT_BIT_VFLIP); -+ u32 resized_height; -+ u32 resize_coeff_v; -+ u32 in_height; -+ -+ tile_idx = row * ctx->in.num_cols; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ resized_height = out_tile->width; -+ else -+ resized_height = out_tile->height; -+ -+ resize_coeff_v = calc_resize_coeff(in_tile->height, -+ ctx->downsize_coeff_v, -+ resized_height, closest); -+ -+ dev_dbg(priv->ipu->dev, "%s: row %u vscale: *8192/%u\n", -+ __func__, row, resize_coeff_v); -+ -+ /* -+ * With the vertical scaling factor known, round up resized -+ * height (output width or height) to IDMAC limitations. -+ */ -+ resized_height = round_up(resized_height, 2); -+ -+ /* -+ * Calculate input width from the last accessed input pixel -+ * given resized height and scaling coefficients. Align to -+ * IDMAC restrictions. -+ */ -+ last_output = resized_height - 1; -+ if (closest && ((last_output * resize_coeff_v) % 8192)) -+ last_output++; -+ in_height = round_up( -+ (DIV_ROUND_UP(last_output * resize_coeff_v, 8192) + 1) -+ << ctx->downsize_coeff_v, 2); -+ -+ for (col = 0; col < ctx->in.num_cols; col++) { -+ tile_idx = row * ctx->in.num_cols + col; -+ in_tile = &ctx->in.tile[tile_idx]; -+ out_tile = &ctx->out.tile[ctx->out_tile_map[tile_idx]]; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ out_tile->width = resized_height; -+ else -+ out_tile->height = resized_height; -+ -+ in_tile->height = in_height; -+ } -+ -+ ctx->resize_coeffs_v[row] = resize_coeff_v; -+ } -+} -+ -+/* -+ * return the number of runs in given queue (pending_q or done_q) -+ * for this context. hold irqlock when calling. -+ */ -+static int get_run_count(struct ipu_image_convert_ctx *ctx, -+ struct list_head *q) -+{ -+ struct ipu_image_convert_run *run; -+ int count = 0; -+ -+ lockdep_assert_held(&ctx->chan->irqlock); -+ -+ list_for_each_entry(run, q, list) { -+ if (run->ctx == ctx) -+ count++; -+ } -+ -+ return count; -+} -+ -+static void convert_stop(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n", -+ __func__, chan->ic_task, ctx, run); -+ -+ /* disable IC tasks and the channels */ -+ ipu_ic_task_disable(chan->ic); -+ ipu_idmac_disable_channel(chan->in_chan); -+ ipu_idmac_disable_channel(chan->out_chan); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_disable_channel(chan->rotation_in_chan); -+ ipu_idmac_disable_channel(chan->rotation_out_chan); -+ ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan); -+ } -+ -+ ipu_ic_disable(chan->ic); -+} -+ -+static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, -+ struct ipuv3_channel *channel, -+ struct ipu_image_convert_image *image, -+ enum ipu_rotate_mode rot_mode, -+ bool rot_swap_width_height, -+ unsigned int tile) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ unsigned int burst_size; -+ u32 width, height, stride; -+ dma_addr_t addr0, addr1 = 0; -+ struct ipu_image tile_image; -+ unsigned int tile_idx[2]; -+ -+ if (image->type == IMAGE_CONVERT_OUT) { -+ tile_idx[0] = ctx->out_tile_map[tile]; -+ tile_idx[1] = ctx->out_tile_map[1]; -+ } else { -+ tile_idx[0] = tile; -+ tile_idx[1] = 1; -+ } -+ -+ if (rot_swap_width_height) { -+ width = image->tile[tile_idx[0]].height; -+ height = image->tile[tile_idx[0]].width; -+ stride = image->tile[tile_idx[0]].rot_stride; -+ addr0 = ctx->rot_intermediate[0].phys; -+ if (ctx->double_buffering) -+ addr1 = ctx->rot_intermediate[1].phys; -+ } else { -+ width = image->tile[tile_idx[0]].width; -+ height = image->tile[tile_idx[0]].height; -+ stride = image->stride; -+ addr0 = image->base.phys0 + -+ image->tile[tile_idx[0]].offset; -+ if (ctx->double_buffering) -+ addr1 = image->base.phys0 + -+ image->tile[tile_idx[1]].offset; -+ } -+ -+ ipu_cpmem_zero(channel); -+ -+ memset(&tile_image, 0, sizeof(tile_image)); -+ tile_image.pix.width = tile_image.rect.width = width; -+ tile_image.pix.height = tile_image.rect.height = height; -+ tile_image.pix.bytesperline = stride; -+ tile_image.pix.pixelformat = image->fmt->fourcc; -+ tile_image.phys0 = addr0; -+ tile_image.phys1 = addr1; -+ if (image->fmt->planar && !rot_swap_width_height) { -+ tile_image.u_offset = image->tile[tile_idx[0]].u_off; -+ tile_image.v_offset = image->tile[tile_idx[0]].v_off; -+ } -+ -+ ipu_cpmem_set_image(channel, &tile_image); -+ -+ if (rot_mode) -+ ipu_cpmem_set_rotation(channel, rot_mode); -+ -+ /* -+ * Skip writing U and V components to odd rows in the output -+ * channels for planar 4:2:0. -+ */ -+ if ((channel == chan->out_chan || -+ channel == chan->rotation_out_chan) && -+ image->fmt->planar && image->fmt->uv_height_dec == 2) -+ ipu_cpmem_skip_odd_chroma_rows(channel); -+ -+ if (channel == chan->rotation_in_chan || -+ channel == chan->rotation_out_chan) { -+ burst_size = 8; -+ ipu_cpmem_set_block_mode(channel); -+ } else -+ burst_size = (width % 16) ? 8 : 16; -+ -+ ipu_cpmem_set_burstsize(channel, burst_size); -+ -+ ipu_ic_task_idma_init(chan->ic, channel, width, height, -+ burst_size, rot_mode); -+ -+ /* -+ * Setting a non-zero AXI ID collides with the PRG AXI snooping, so -+ * only do this when there is no PRG present. -+ */ -+ if (!channel->ipu->prg_priv) -+ ipu_cpmem_set_axi_id(channel, 1); -+ -+ ipu_idmac_set_double_buffer(channel, ctx->double_buffering); -+} -+ -+static int convert_start(struct ipu_image_convert_run *run, unsigned int tile) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ unsigned int dst_tile = ctx->out_tile_map[tile]; -+ unsigned int dest_width, dest_height; -+ unsigned int col, row; -+ u32 rsc; -+ int ret; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p tile %u -> %u\n", -+ __func__, chan->ic_task, ctx, run, tile, dst_tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* swap width/height for resizer */ -+ dest_width = d_image->tile[dst_tile].height; -+ dest_height = d_image->tile[dst_tile].width; -+ } else { -+ dest_width = d_image->tile[dst_tile].width; -+ dest_height = d_image->tile[dst_tile].height; -+ } -+ -+ row = tile / s_image->num_cols; -+ col = tile % s_image->num_cols; -+ -+ rsc = (ctx->downsize_coeff_v << 30) | -+ (ctx->resize_coeffs_v[row] << 16) | -+ (ctx->downsize_coeff_h << 14) | -+ (ctx->resize_coeffs_h[col]); -+ -+ dev_dbg(priv->ipu->dev, "%s: %ux%u -> %ux%u (rsc = 0x%x)\n", -+ __func__, s_image->tile[tile].width, -+ s_image->tile[tile].height, dest_width, dest_height, rsc); -+ -+ /* setup the IC resizer and CSC */ -+ ret = ipu_ic_task_init_rsc(chan->ic, &ctx->csc, -+ s_image->tile[tile].width, -+ s_image->tile[tile].height, -+ dest_width, -+ dest_height, -+ rsc); -+ if (ret) { -+ dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret); -+ return ret; -+ } -+ -+ /* init the source MEM-->IC PP IDMAC channel */ -+ init_idmac_channel(ctx, chan->in_chan, s_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* init the IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ IPU_ROTATE_NONE, true, tile); -+ -+ /* init the MEM-->IC PP ROT IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_in_chan, d_image, -+ ctx->rot_mode, true, tile); -+ -+ /* init the destination IC PP ROT-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->rotation_out_chan, d_image, -+ IPU_ROTATE_NONE, false, tile); -+ -+ /* now link IC PP-->MEM to MEM-->IC PP ROT */ -+ ipu_idmac_link(chan->out_chan, chan->rotation_in_chan); -+ } else { -+ /* init the destination IC PP-->MEM IDMAC channel */ -+ init_idmac_channel(ctx, chan->out_chan, d_image, -+ ctx->rot_mode, false, tile); -+ } -+ -+ /* enable the IC */ -+ ipu_ic_enable(chan->ic); -+ -+ /* set buffers ready */ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(chan->out_chan, 0); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 0); -+ if (ctx->double_buffering) { -+ ipu_idmac_select_buffer(chan->in_chan, 1); -+ ipu_idmac_select_buffer(chan->out_chan, 1); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) -+ ipu_idmac_select_buffer(chan->rotation_out_chan, 1); -+ } -+ -+ /* enable the channels! */ -+ ipu_idmac_enable_channel(chan->in_chan); -+ ipu_idmac_enable_channel(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_idmac_enable_channel(chan->rotation_in_chan); -+ ipu_idmac_enable_channel(chan->rotation_out_chan); -+ } -+ -+ ipu_ic_task_enable(chan->ic); -+ -+ ipu_cpmem_dump(chan->in_chan); -+ ipu_cpmem_dump(chan->out_chan); -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ ipu_cpmem_dump(chan->rotation_in_chan); -+ ipu_cpmem_dump(chan->rotation_out_chan); -+ } -+ -+ ipu_dump(priv->ipu); -+ -+ return 0; -+} -+ -+/* hold irqlock when calling */ -+static int do_run(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ ctx->in.base.phys0 = run->in_phys; -+ ctx->out.base.phys0 = run->out_phys; -+ -+ ctx->cur_buf_num = 0; -+ ctx->next_tile = 1; -+ -+ /* remove run from pending_q and set as current */ -+ list_del(&run->list); -+ chan->current_run = run; -+ -+ return convert_start(run, 0); -+} -+ -+/* hold irqlock when calling */ -+static void run_next(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *tmp; -+ int ret; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ /* skip contexts that are aborting */ -+ if (run->ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: skipping aborting ctx %p run %p\n", -+ __func__, chan->ic_task, run->ctx, run); -+ continue; -+ } -+ -+ ret = do_run(run); -+ if (!ret) -+ break; -+ -+ /* -+ * something went wrong with start, add the run -+ * to done q and continue to the next run in the -+ * pending q. -+ */ -+ run->status = ret; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ } -+} -+ -+static void empty_done_q(struct ipu_image_convert_chan *chan) -+{ -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ while (!list_empty(&chan->done_q)) { -+ run = list_entry(chan->done_q.next, -+ struct ipu_image_convert_run, -+ list); -+ -+ list_del(&run->list); -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: completing ctx %p run %p with %d\n", -+ __func__, chan->ic_task, run->ctx, run, run->status); -+ -+ /* call the completion callback and free the run */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ run->ctx->complete(run, run->ctx->complete_context); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+} -+ -+/* -+ * the bottom half thread clears out the done_q, calling the -+ * completion handler for each. -+ */ -+static irqreturn_t do_bh(int irq, void *dev_id) -+{ -+ struct ipu_image_convert_chan *chan = dev_id; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__, -+ chan->ic_task); -+ -+ empty_done_q(chan); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* -+ * the done_q is cleared out, signal any contexts -+ * that are aborting that abort can complete. -+ */ -+ list_for_each_entry(ctx, &chan->ctx_list, list) { -+ if (ctx->aborting) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: signaling abort for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ complete_all(&ctx->aborted); -+ } -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__, -+ chan->ic_task); -+ -+ return IRQ_HANDLED; -+} -+ -+static bool ic_settings_changed(struct ipu_image_convert_ctx *ctx) -+{ -+ unsigned int cur_tile = ctx->next_tile - 1; -+ unsigned int next_tile = ctx->next_tile; -+ -+ if (ctx->resize_coeffs_h[cur_tile % ctx->in.num_cols] != -+ ctx->resize_coeffs_h[next_tile % ctx->in.num_cols] || -+ ctx->resize_coeffs_v[cur_tile / ctx->in.num_cols] != -+ ctx->resize_coeffs_v[next_tile / ctx->in.num_cols] || -+ ctx->in.tile[cur_tile].width != ctx->in.tile[next_tile].width || -+ ctx->in.tile[cur_tile].height != ctx->in.tile[next_tile].height || -+ ctx->out.tile[cur_tile].width != ctx->out.tile[next_tile].width || -+ ctx->out.tile[cur_tile].height != ctx->out.tile[next_tile].height) -+ return true; -+ -+ return false; -+} -+ -+/* hold irqlock when calling */ -+static irqreturn_t do_irq(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_ctx *ctx = run->ctx; -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_tile *src_tile, *dst_tile; -+ struct ipu_image_convert_image *s_image = &ctx->in; -+ struct ipu_image_convert_image *d_image = &ctx->out; -+ struct ipuv3_channel *outch; -+ unsigned int dst_idx; -+ -+ lockdep_assert_held(&chan->irqlock); -+ -+ outch = ipu_rot_mode_is_irt(ctx->rot_mode) ? -+ chan->rotation_out_chan : chan->out_chan; -+ -+ /* -+ * It is difficult to stop the channel DMA before the channels -+ * enter the paused state. Without double-buffering the channels -+ * are always in a paused state when the EOF irq occurs, so it -+ * is safe to stop the channels now. For double-buffering we -+ * just ignore the abort until the operation completes, when it -+ * is safe to shut down. -+ */ -+ if (ctx->aborting && !ctx->double_buffering) { -+ convert_stop(run); -+ run->status = -EIO; -+ goto done; -+ } -+ -+ if (ctx->next_tile == ctx->num_tiles) { -+ /* -+ * the conversion is complete -+ */ -+ convert_stop(run); -+ run->status = 0; -+ goto done; -+ } -+ -+ /* -+ * not done, place the next tile buffers. -+ */ -+ if (!ctx->double_buffering) { -+ if (ic_settings_changed(ctx)) { -+ convert_stop(run); -+ convert_start(run, ctx->next_tile); -+ } else { -+ src_tile = &s_image->tile[ctx->next_tile]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, 0, -+ s_image->base.phys0 + -+ src_tile->offset); -+ ipu_cpmem_set_buffer(outch, 0, -+ d_image->base.phys0 + -+ dst_tile->offset); -+ if (s_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(chan->in_chan, -+ src_tile->u_off, -+ src_tile->v_off); -+ if (d_image->fmt->planar) -+ ipu_cpmem_set_uv_offset(outch, -+ dst_tile->u_off, -+ dst_tile->v_off); -+ -+ ipu_idmac_select_buffer(chan->in_chan, 0); -+ ipu_idmac_select_buffer(outch, 0); -+ } -+ } else if (ctx->next_tile < ctx->num_tiles - 1) { -+ -+ src_tile = &s_image->tile[ctx->next_tile + 1]; -+ dst_idx = ctx->out_tile_map[ctx->next_tile + 1]; -+ dst_tile = &d_image->tile[dst_idx]; -+ -+ ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num, -+ s_image->base.phys0 + src_tile->offset); -+ ipu_cpmem_set_buffer(outch, ctx->cur_buf_num, -+ d_image->base.phys0 + dst_tile->offset); -+ -+ ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num); -+ ipu_idmac_select_buffer(outch, ctx->cur_buf_num); -+ -+ ctx->cur_buf_num ^= 1; -+ } -+ -+ ctx->next_tile++; -+ return IRQ_HANDLED; -+done: -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ return IRQ_WAKE_THREAD; -+} -+ -+static irqreturn_t norotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this is a rotation operation, just ignore */ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+static irqreturn_t rotate_irq(int irq, void *data) -+{ -+ struct ipu_image_convert_chan *chan = data; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ irqreturn_t ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* get current run and its context */ -+ run = chan->current_run; -+ if (!run) { -+ ret = IRQ_NONE; -+ goto out; -+ } -+ -+ ctx = run->ctx; -+ -+ if (!ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ /* this was NOT a rotation operation, shouldn't happen */ -+ dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n"); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return IRQ_HANDLED; -+ } -+ -+ ret = do_irq(run); -+out: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+ -+/* -+ * try to force the completion of runs for this ctx. Called when -+ * abort wait times out in ipu_image_convert_abort(). -+ */ -+static void force_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_run *run; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ run = chan->current_run; -+ if (run && run->ctx == ctx) { -+ convert_stop(run); -+ run->status = -EIO; -+ list_add_tail(&run->list, &chan->done_q); -+ chan->current_run = NULL; -+ run_next(chan); -+ } -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ empty_done_q(chan); -+} -+ -+static void release_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ if (chan->out_eof_irq >= 0) -+ free_irq(chan->out_eof_irq, chan); -+ if (chan->rot_out_eof_irq >= 0) -+ free_irq(chan->rot_out_eof_irq, chan); -+ -+ if (!IS_ERR_OR_NULL(chan->in_chan)) -+ ipu_idmac_put(chan->in_chan); -+ if (!IS_ERR_OR_NULL(chan->out_chan)) -+ ipu_idmac_put(chan->out_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_in_chan)) -+ ipu_idmac_put(chan->rotation_in_chan); -+ if (!IS_ERR_OR_NULL(chan->rotation_out_chan)) -+ ipu_idmac_put(chan->rotation_out_chan); -+ if (!IS_ERR_OR_NULL(chan->ic)) -+ ipu_ic_put(chan->ic); -+ -+ chan->in_chan = chan->out_chan = chan->rotation_in_chan = -+ chan->rotation_out_chan = NULL; -+ chan->out_eof_irq = chan->rot_out_eof_irq = -1; -+} -+ -+static int get_ipu_resources(struct ipu_image_convert_chan *chan) -+{ -+ const struct ipu_image_convert_dma_chan *dma = chan->dma_ch; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ int ret; -+ -+ /* get IC */ -+ chan->ic = ipu_ic_get(priv->ipu, chan->ic_task); -+ if (IS_ERR(chan->ic)) { -+ dev_err(priv->ipu->dev, "could not acquire IC\n"); -+ ret = PTR_ERR(chan->ic); -+ goto err; -+ } -+ -+ /* get IDMAC channels */ -+ chan->in_chan = ipu_idmac_get(priv->ipu, dma->in); -+ chan->out_chan = ipu_idmac_get(priv->ipu, dma->out); -+ if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) { -+ dev_err(priv->ipu->dev, "could not acquire idmac channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in); -+ chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out); -+ if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) { -+ dev_err(priv->ipu->dev, -+ "could not acquire idmac rotation channels\n"); -+ ret = -EBUSY; -+ goto err; -+ } -+ -+ /* acquire the EOF interrupts */ -+ chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->out_eof_irq); -+ chan->out_eof_irq = -1; -+ goto err; -+ } -+ -+ chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu, -+ chan->rotation_out_chan, -+ IPU_IRQ_EOF); -+ -+ ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh, -+ 0, "ipu-ic", chan); -+ if (ret < 0) { -+ dev_err(priv->ipu->dev, "could not acquire irq %d\n", -+ chan->rot_out_eof_irq); -+ chan->rot_out_eof_irq = -1; -+ goto err; -+ } -+ -+ return 0; -+err: -+ release_ipu_resources(chan); -+ return ret; -+} -+ -+static int fill_image(struct ipu_image_convert_ctx *ctx, -+ struct ipu_image_convert_image *ic_image, -+ struct ipu_image *image, -+ enum ipu_image_convert_type type) -+{ -+ struct ipu_image_convert_priv *priv = ctx->chan->priv; -+ -+ ic_image->base = *image; -+ ic_image->type = type; -+ -+ ic_image->fmt = get_format(image->pix.pixelformat); -+ if (!ic_image->fmt) { -+ dev_err(priv->ipu->dev, "pixelformat not supported for %s\n", -+ type == IMAGE_CONVERT_OUT ? "Output" : "Input"); -+ return -EINVAL; -+ } -+ -+ if (ic_image->fmt->planar) -+ ic_image->stride = ic_image->base.pix.width; -+ else -+ ic_image->stride = ic_image->base.pix.bytesperline; -+ -+ return 0; -+} -+ -+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */ -+static unsigned int clamp_align(unsigned int x, unsigned int min, -+ unsigned int max, unsigned int align) -+{ -+ /* Bits that must be zero to be aligned */ -+ unsigned int mask = ~((1 << align) - 1); -+ -+ /* Clamp to aligned min and max */ -+ x = clamp(x, (min + ~mask) & mask, max & mask); -+ -+ /* Round to nearest aligned value */ -+ if (align) -+ x = (x + (1 << (align - 1))) & mask; -+ -+ return x; -+} -+ -+/* Adjusts input/output images to IPU restrictions */ -+void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ const struct ipu_image_pixfmt *infmt, *outfmt; -+ u32 w_align_out, h_align_out; -+ u32 w_align_in, h_align_in; -+ -+ infmt = get_format(in->pix.pixelformat); -+ outfmt = get_format(out->pix.pixelformat); -+ -+ /* set some default pixel formats if needed */ -+ if (!infmt) { -+ in->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ infmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ if (!outfmt) { -+ out->pix.pixelformat = V4L2_PIX_FMT_RGB24; -+ outfmt = get_format(V4L2_PIX_FMT_RGB24); -+ } -+ -+ /* image converter does not handle fields */ -+ in->pix.field = out->pix.field = V4L2_FIELD_NONE; -+ -+ /* resizer cannot downsize more than 4:1 */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.width / 4); -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.height / 4); -+ } else { -+ out->pix.width = max_t(__u32, out->pix.width, -+ in->pix.width / 4); -+ out->pix.height = max_t(__u32, out->pix.height, -+ in->pix.height / 4); -+ } -+ -+ /* align input width/height */ -+ w_align_in = ilog2(tile_width_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ h_align_in = ilog2(tile_height_align(IMAGE_CONVERT_IN, infmt, -+ rot_mode)); -+ in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, -+ w_align_in); -+ in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, -+ h_align_in); -+ -+ /* align output width/height */ -+ w_align_out = ilog2(tile_width_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ h_align_out = ilog2(tile_height_align(IMAGE_CONVERT_OUT, outfmt, -+ rot_mode)); -+ out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, -+ w_align_out); -+ out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, -+ h_align_out); -+ -+ /* set input/output strides and image sizes */ -+ in->pix.bytesperline = infmt->planar ? -+ clamp_align(in->pix.width, 2 << w_align_in, MAX_W, -+ w_align_in) : -+ clamp_align((in->pix.width * infmt->bpp) >> 3, -+ ((2 << w_align_in) * infmt->bpp) >> 3, -+ (MAX_W * infmt->bpp) >> 3, -+ w_align_in); -+ in->pix.sizeimage = infmt->planar ? -+ (in->pix.height * in->pix.bytesperline * infmt->bpp) >> 3 : -+ in->pix.height * in->pix.bytesperline; -+ out->pix.bytesperline = outfmt->planar ? out->pix.width : -+ (out->pix.width * outfmt->bpp) >> 3; -+ out->pix.sizeimage = outfmt->planar ? -+ (out->pix.height * out->pix.bytesperline * outfmt->bpp) >> 3 : -+ out->pix.height * out->pix.bytesperline; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust); -+ -+/* -+ * this is used by ipu_image_convert_prepare() to verify set input and -+ * output images are valid before starting the conversion. Clients can -+ * also call it before calling ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image testin, testout; -+ -+ testin = *in; -+ testout = *out; -+ -+ ipu_image_convert_adjust(&testin, &testout, rot_mode); -+ -+ if (testin.pix.width != in->pix.width || -+ testin.pix.height != in->pix.height || -+ testout.pix.width != out->pix.width || -+ testout.pix.height != out->pix.height) -+ return -EINVAL; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_verify); -+ -+/* -+ * Call ipu_image_convert_prepare() to prepare for the conversion of -+ * given images and rotation mode. Returns a new conversion context. -+ */ -+struct ipu_image_convert_ctx * -+ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_priv *priv = ipu->image_convert_priv; -+ struct ipu_image_convert_image *s_image, *d_image; -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ unsigned int i; -+ bool get_res; -+ int ret; -+ -+ if (!in || !out || !complete || -+ (ic_task != IC_TASK_VIEWFINDER && -+ ic_task != IC_TASK_POST_PROCESSOR)) -+ return ERR_PTR(-EINVAL); -+ -+ /* verify the in/out images before continuing */ -+ ret = ipu_image_convert_verify(in, out, rot_mode); -+ if (ret) { -+ dev_err(priv->ipu->dev, "%s: in/out formats invalid\n", -+ __func__); -+ return ERR_PTR(ret); -+ } -+ -+ chan = &priv->chan[ic_task]; -+ -+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); -+ if (!ctx) -+ return ERR_PTR(-ENOMEM); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ ctx->chan = chan; -+ init_completion(&ctx->aborted); -+ -+ ctx->rot_mode = rot_mode; -+ -+ /* Sets ctx->in.num_rows/cols as well */ -+ ret = calc_image_resize_coefficients(ctx, in, out); -+ if (ret) -+ goto out_free; -+ -+ s_image = &ctx->in; -+ d_image = &ctx->out; -+ -+ /* set tiling and rotation */ -+ if (ipu_rot_mode_is_irt(rot_mode)) { -+ d_image->num_rows = s_image->num_cols; -+ d_image->num_cols = s_image->num_rows; -+ } else { -+ d_image->num_rows = s_image->num_rows; -+ d_image->num_cols = s_image->num_cols; -+ } -+ -+ ctx->num_tiles = d_image->num_cols * d_image->num_rows; -+ -+ ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN); -+ if (ret) -+ goto out_free; -+ ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT); -+ if (ret) -+ goto out_free; -+ -+ calc_out_tile_map(ctx); -+ -+ find_seams(ctx, s_image, d_image); -+ -+ ret = calc_tile_dimensions(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ ret = calc_tile_offsets(ctx, s_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_dimensions(ctx, d_image); -+ ret = calc_tile_offsets(ctx, d_image); -+ if (ret) -+ goto out_free; -+ -+ calc_tile_resize_coefficients(ctx); -+ -+ ret = ipu_ic_calc_csc(&ctx->csc, -+ s_image->base.pix.ycbcr_enc, -+ s_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(s_image->fmt->fourcc), -+ d_image->base.pix.ycbcr_enc, -+ d_image->base.pix.quantization, -+ ipu_pixelformat_to_colorspace(d_image->fmt->fourcc)); -+ if (ret) -+ goto out_free; -+ -+ dump_format(ctx, s_image); -+ dump_format(ctx, d_image); -+ -+ ctx->complete = complete; -+ ctx->complete_context = complete_context; -+ -+ /* -+ * Can we use double-buffering for this operation? If there is -+ * only one tile (the whole image can be converted in a single -+ * operation) there's no point in using double-buffering. Also, -+ * the IPU's IDMAC channels allow only a single U and V plane -+ * offset shared between both buffers, but these offsets change -+ * for every tile, and therefore would have to be updated for -+ * each buffer which is not possible. So double-buffering is -+ * impossible when either the source or destination images are -+ * a planar format (YUV420, YUV422P, etc.). Further, differently -+ * sized tiles or different resizing coefficients per tile -+ * prevent double-buffering as well. -+ */ -+ ctx->double_buffering = (ctx->num_tiles > 1 && -+ !s_image->fmt->planar && -+ !d_image->fmt->planar); -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (ctx->in.tile[i].width != ctx->in.tile[0].width || -+ ctx->in.tile[i].height != ctx->in.tile[0].height || -+ ctx->out.tile[i].width != ctx->out.tile[0].width || -+ ctx->out.tile[i].height != ctx->out.tile[0].height) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_cols; i++) { -+ if (ctx->resize_coeffs_h[i] != ctx->resize_coeffs_h[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ for (i = 1; i < ctx->in.num_rows; i++) { -+ if (ctx->resize_coeffs_v[i] != ctx->resize_coeffs_v[0]) { -+ ctx->double_buffering = false; -+ break; -+ } -+ } -+ -+ if (ipu_rot_mode_is_irt(ctx->rot_mode)) { -+ unsigned long intermediate_size = d_image->tile[0].size; -+ -+ for (i = 1; i < ctx->num_tiles; i++) { -+ if (d_image->tile[i].size > intermediate_size) -+ intermediate_size = d_image->tile[i].size; -+ } -+ -+ ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0], -+ intermediate_size); -+ if (ret) -+ goto out_free; -+ if (ctx->double_buffering) { -+ ret = alloc_dma_buf(priv, -+ &ctx->rot_intermediate[1], -+ intermediate_size); -+ if (ret) -+ goto out_free_dmabuf0; -+ } -+ } -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ get_res = list_empty(&chan->ctx_list); -+ -+ list_add_tail(&ctx->list, &chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (get_res) { -+ ret = get_ipu_resources(chan); -+ if (ret) -+ goto out_free_dmabuf1; -+ } -+ -+ return ctx; -+ -+out_free_dmabuf1: -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ spin_lock_irqsave(&chan->irqlock, flags); -+ list_del(&ctx->list); -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+out_free_dmabuf0: -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+out_free: -+ kfree(ctx); -+ return ERR_PTR(ret); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare); -+ -+/* -+ * Carry out a single image conversion run. Only the physaddr's of the input -+ * and output image buffers are needed. The conversion context must have -+ * been created previously with ipu_image_convert_prepare(). -+ */ -+int ipu_image_convert_queue(struct ipu_image_convert_run *run) -+{ -+ struct ipu_image_convert_chan *chan; -+ struct ipu_image_convert_priv *priv; -+ struct ipu_image_convert_ctx *ctx; -+ unsigned long flags; -+ int ret = 0; -+ -+ if (!run || !run->ctx || !run->in_phys || !run->out_phys) -+ return -EINVAL; -+ -+ ctx = run->ctx; -+ chan = ctx->chan; -+ priv = chan->priv; -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__, -+ chan->ic_task, ctx, run); -+ -+ INIT_LIST_HEAD(&run->list); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ if (ctx->aborting) { -+ ret = -EIO; -+ goto unlock; -+ } -+ -+ list_add_tail(&run->list, &chan->pending_q); -+ -+ if (!chan->current_run) { -+ ret = do_run(run); -+ if (ret) -+ chan->current_run = NULL; -+ } -+unlock: -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_queue); -+ -+/* Abort any active or pending conversions for this context */ -+static void __ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ struct ipu_image_convert_run *run, *active_run, *tmp; -+ unsigned long flags; -+ int run_count, ret; -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ /* move all remaining pending runs in this context to done_q */ -+ list_for_each_entry_safe(run, tmp, &chan->pending_q, list) { -+ if (run->ctx != ctx) -+ continue; -+ run->status = -EIO; -+ list_move_tail(&run->list, &chan->done_q); -+ } -+ -+ run_count = get_run_count(ctx, &chan->done_q); -+ active_run = (chan->current_run && chan->current_run->ctx == ctx) ? -+ chan->current_run : NULL; -+ -+ if (active_run) -+ reinit_completion(&ctx->aborted); -+ -+ ctx->aborting = true; -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (!run_count && !active_run) { -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: no abort needed for ctx %p\n", -+ __func__, chan->ic_task, ctx); -+ return; -+ } -+ -+ if (!active_run) { -+ empty_done_q(chan); -+ return; -+ } -+ -+ dev_dbg(priv->ipu->dev, -+ "%s: task %u: wait for completion: %d runs\n", -+ __func__, chan->ic_task, run_count); -+ -+ ret = wait_for_completion_timeout(&ctx->aborted, -+ msecs_to_jiffies(10000)); -+ if (ret == 0) { -+ dev_warn(priv->ipu->dev, "%s: timeout\n", __func__); -+ force_abort(ctx); -+ } -+} -+ -+void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx) -+{ -+ __ipu_image_convert_abort(ctx); -+ ctx->aborting = false; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_abort); -+ -+/* Unprepare image conversion context */ -+void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx) -+{ -+ struct ipu_image_convert_chan *chan = ctx->chan; -+ struct ipu_image_convert_priv *priv = chan->priv; -+ unsigned long flags; -+ bool put_res; -+ -+ /* make sure no runs are hanging around */ -+ __ipu_image_convert_abort(ctx); -+ -+ dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__, -+ chan->ic_task, ctx); -+ -+ spin_lock_irqsave(&chan->irqlock, flags); -+ -+ list_del(&ctx->list); -+ -+ put_res = list_empty(&chan->ctx_list); -+ -+ spin_unlock_irqrestore(&chan->irqlock, flags); -+ -+ if (put_res) -+ release_ipu_resources(chan); -+ -+ free_dma_buf(priv, &ctx->rot_intermediate[1]); -+ free_dma_buf(priv, &ctx->rot_intermediate[0]); -+ -+ kfree(ctx); -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare); -+ -+/* -+ * "Canned" asynchronous single image conversion. Allocates and returns -+ * a new conversion run. On successful return the caller must free the -+ * run and call ipu_image_convert_unprepare() after conversion completes. -+ */ -+struct ipu_image_convert_run * -+ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode, -+ ipu_image_convert_cb_t complete, -+ void *complete_context) -+{ -+ struct ipu_image_convert_ctx *ctx; -+ struct ipu_image_convert_run *run; -+ int ret; -+ -+ ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode, -+ complete, complete_context); -+ if (IS_ERR(ctx)) -+ return ERR_CAST(ctx); -+ -+ run = kzalloc(sizeof(*run), GFP_KERNEL); -+ if (!run) { -+ ipu_image_convert_unprepare(ctx); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ run->ctx = ctx; -+ run->in_phys = in->phys0; -+ run->out_phys = out->phys0; -+ -+ ret = ipu_image_convert_queue(run); -+ if (ret) { -+ ipu_image_convert_unprepare(ctx); -+ kfree(run); -+ return ERR_PTR(ret); -+ } -+ -+ return run; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert); -+ -+/* "Canned" synchronous single image conversion */ -+static void image_convert_sync_complete(struct ipu_image_convert_run *run, -+ void *data) -+{ -+ struct completion *comp = data; -+ -+ complete(comp); -+} -+ -+int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task, -+ struct ipu_image *in, struct ipu_image *out, -+ enum ipu_rotate_mode rot_mode) -+{ -+ struct ipu_image_convert_run *run; -+ struct completion comp; -+ int ret; -+ -+ init_completion(&comp); -+ -+ run = ipu_image_convert(ipu, ic_task, in, out, rot_mode, -+ image_convert_sync_complete, &comp); -+ if (IS_ERR(run)) -+ return PTR_ERR(run); -+ -+ ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000)); -+ ret = (ret == 0) ? -ETIMEDOUT : 0; -+ -+ ipu_image_convert_unprepare(run->ctx); -+ kfree(run); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_image_convert_sync); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev) -+{ -+ struct ipu_image_convert_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->image_convert_priv = priv; -+ priv->ipu = ipu; -+ -+ for (i = 0; i < IC_NUM_TASKS; i++) { -+ struct ipu_image_convert_chan *chan = &priv->chan[i]; -+ -+ chan->ic_task = i; -+ chan->priv = priv; -+ chan->dma_ch = &image_convert_dma_chan[i]; -+ chan->out_eof_irq = -1; -+ chan->rot_out_eof_irq = -1; -+ -+ spin_lock_init(&chan->irqlock); -+ INIT_LIST_HEAD(&chan->ctx_list); -+ INIT_LIST_HEAD(&chan->pending_q); -+ INIT_LIST_HEAD(&chan->done_q); -+ } -+ -+ return 0; -+} -+ -+void ipu_image_convert_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-pre.c -@@ -0,0 +1,346 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/genalloc.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRE_MAX_WIDTH 2048 -+#define IPU_PRE_NUM_SCANLINES 8 -+ -+#define IPU_PRE_CTRL 0x000 -+#define IPU_PRE_CTRL_SET 0x004 -+#define IPU_PRE_CTRL_ENABLE (1 << 0) -+#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) -+#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) -+#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) -+#define IPU_PRE_CTRL_VFLIP (1 << 5) -+#define IPU_PRE_CTRL_SO (1 << 6) -+#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) -+#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) -+#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) -+#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) -+#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) -+#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) -+#define IPU_PRE_CTRL_CLKGATE (1 << 30) -+#define IPU_PRE_CTRL_SFTRST (1 << 31) -+ -+#define IPU_PRE_CUR_BUF 0x030 -+ -+#define IPU_PRE_NEXT_BUF 0x040 -+ -+#define IPU_PRE_TPR_CTRL 0x070 -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT (1 << 0) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SPLIT_BUF (1 << 4) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF (1 << 5) -+#define IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED (1 << 6) -+ -+#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 -+#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) -+#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) -+#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) -+#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) -+#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) -+#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) -+ -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 -+#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) -+#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_CTRL 0x110 -+#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) -+#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) -+#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) -+ -+#define IPU_PRE_STORE_ENG_STATUS 0x120 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_MASK 0xffff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_X_SHIFT 0 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK 0x3fff -+#define IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT 16 -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIFO_FULL (1 << 30) -+#define IPU_PRE_STORE_ENG_STATUS_STORE_FIELD (1 << 31) -+ -+#define IPU_PRE_STORE_ENG_SIZE 0x130 -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) -+#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) -+ -+#define IPU_PRE_STORE_ENG_PITCH 0x140 -+#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) -+ -+#define IPU_PRE_STORE_ENG_ADDR 0x150 -+ -+struct ipu_pre { -+ struct list_head list; -+ struct device *dev; -+ -+ void __iomem *regs; -+ struct clk *clk_axi; -+ struct gen_pool *iram; -+ -+ dma_addr_t buffer_paddr; -+ void *buffer_virt; -+ bool in_use; -+ unsigned int safe_window_end; -+ unsigned int last_bufaddr; -+}; -+ -+static DEFINE_MUTEX(ipu_pre_list_mutex); -+static LIST_HEAD(ipu_pre_list); -+static int available_pres; -+ -+int ipu_pre_get_available_count(void) -+{ -+ return available_pres; -+} -+ -+struct ipu_pre * -+ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index) -+{ -+ struct device_node *pre_node = of_parse_phandle(dev->of_node, -+ name, index); -+ struct ipu_pre *pre; -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_for_each_entry(pre, &ipu_pre_list, list) { -+ if (pre_node == pre->dev->of_node) { -+ mutex_unlock(&ipu_pre_list_mutex); -+ device_link_add(dev, pre->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ of_node_put(pre_node); -+ return pre; -+ } -+ } -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ of_node_put(pre_node); -+ -+ return NULL; -+} -+ -+int ipu_pre_get(struct ipu_pre *pre) -+{ -+ u32 val; -+ -+ if (pre->in_use) -+ return -EBUSY; -+ -+ /* first get the engine out of reset and remove clock gating */ -+ writel(0, pre->regs + IPU_PRE_CTRL); -+ -+ /* init defaults that should be applied to all streams */ -+ val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | -+ IPU_PRE_CTRL_HANDSHAKE_EN | -+ IPU_PRE_CTRL_TPR_REST_SEL | -+ IPU_PRE_CTRL_SDW_UPDATE; -+ writel(val, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = true; -+ return 0; -+} -+ -+void ipu_pre_put(struct ipu_pre *pre) -+{ -+ writel(IPU_PRE_CTRL_SFTRST, pre->regs + IPU_PRE_CTRL); -+ -+ pre->in_use = false; -+} -+ -+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -+ unsigned int height, unsigned int stride, u32 format, -+ uint64_t modifier, unsigned int bufaddr) -+{ -+ const struct drm_format_info *info = drm_format_info(format); -+ u32 active_bpp = info->cpp[0] >> 1; -+ u32 val; -+ -+ /* calculate safe window for ctrl register updates */ -+ if (modifier == DRM_FORMAT_MOD_LINEAR) -+ pre->safe_window_end = height - 2; -+ else -+ pre->safe_window_end = DIV_ROUND_UP(height, 4) - 1; -+ -+ writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); -+ writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -+ pre->last_bufaddr = bufaddr; -+ -+ val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | -+ IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | -+ IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | -+ IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); -+ -+ val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | -+ IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); -+ -+ val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); -+ writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); -+ -+ val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | -+ IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | -+ IPU_PRE_STORE_ENG_CTRL_STORE_EN; -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); -+ -+ val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | -+ IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); -+ -+ val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); -+ writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); -+ -+ writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); -+ -+ val = readl(pre->regs + IPU_PRE_TPR_CTRL); -+ val &= ~IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK; -+ if (modifier != DRM_FORMAT_MOD_LINEAR) { -+ /* only support single buffer formats for now */ -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SINGLE_BUF; -+ if (modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED) -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_SUPER_TILED; -+ if (info->cpp[0] == 2) -+ val |= IPU_PRE_TPR_CTRL_TILE_FORMAT_16_BIT; -+ } -+ writel(val, pre->regs + IPU_PRE_TPR_CTRL); -+ -+ val = readl(pre->regs + IPU_PRE_CTRL); -+ val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | -+ IPU_PRE_CTRL_SDW_UPDATE; -+ if (modifier == DRM_FORMAT_MOD_LINEAR) -+ val &= ~IPU_PRE_CTRL_BLOCK_EN; -+ else -+ val |= IPU_PRE_CTRL_BLOCK_EN; -+ writel(val, pre->regs + IPU_PRE_CTRL); -+} -+ -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) -+{ -+ unsigned long timeout = jiffies + msecs_to_jiffies(5); -+ unsigned short current_yblock; -+ u32 val; -+ -+ if (bufaddr == pre->last_bufaddr) -+ return; -+ -+ writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); -+ pre->last_bufaddr = bufaddr; -+ -+ do { -+ if (time_after(jiffies, timeout)) { -+ dev_warn(pre->dev, "timeout waiting for PRE safe window\n"); -+ return; -+ } -+ -+ val = readl(pre->regs + IPU_PRE_STORE_ENG_STATUS); -+ current_yblock = -+ (val >> IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_SHIFT) & -+ IPU_PRE_STORE_ENG_STATUS_STORE_BLOCK_Y_MASK; -+ } while (current_yblock == 0 || current_yblock >= pre->safe_window_end); -+ -+ writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); -+} -+ -+bool ipu_pre_update_pending(struct ipu_pre *pre) -+{ -+ return !!(readl_relaxed(pre->regs + IPU_PRE_CTRL) & -+ IPU_PRE_CTRL_SDW_UPDATE); -+} -+ -+u32 ipu_pre_get_baddr(struct ipu_pre *pre) -+{ -+ return (u32)pre->buffer_paddr; -+} -+ -+static int ipu_pre_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_pre *pre; -+ -+ pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); -+ if (!pre) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ pre->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(pre->regs)) -+ return PTR_ERR(pre->regs); -+ -+ pre->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(pre->clk_axi)) -+ return PTR_ERR(pre->clk_axi); -+ -+ pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0); -+ if (!pre->iram) -+ return -EPROBE_DEFER; -+ -+ /* -+ * Allocate IRAM buffer with maximum size. This could be made dynamic, -+ * but as there is no other user of this IRAM region and we can fit all -+ * max sized buffers into it, there is no need yet. -+ */ -+ pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH * -+ IPU_PRE_NUM_SCANLINES * 4, -+ &pre->buffer_paddr); -+ if (!pre->buffer_virt) -+ return -ENOMEM; -+ -+ clk_prepare_enable(pre->clk_axi); -+ -+ pre->dev = dev; -+ platform_set_drvdata(pdev, pre); -+ mutex_lock(&ipu_pre_list_mutex); -+ list_add(&pre->list, &ipu_pre_list); -+ available_pres++; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_pre_remove(struct platform_device *pdev) -+{ -+ struct ipu_pre *pre = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_pre_list_mutex); -+ list_del(&pre->list); -+ available_pres--; -+ mutex_unlock(&ipu_pre_list_mutex); -+ -+ clk_disable_unprepare(pre->clk_axi); -+ -+ if (pre->buffer_virt) -+ gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt, -+ IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); -+ return 0; -+} -+ -+static const struct of_device_id ipu_pre_dt_ids[] = { -+ { .compatible = "fsl,imx6qp-pre", }, -+ { /* sentinel */ }, -+}; -+ -+struct platform_driver ipu_pre_drv = { -+ .probe = ipu_pre_probe, -+ .remove = ipu_pre_remove, -+ .driver = { -+ .name = "imx-ipu-pre", -+ .of_match_table = ipu_pre_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-prg.c -@@ -0,0 +1,483 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (c) 2016-2017 Lucas Stach, Pengutronix -+ */ -+ -+#include <drm/drm_fourcc.h> -+#include <linux/clk.h> -+#include <linux/err.h> -+#include <linux/iopoll.h> -+#include <linux/mfd/syscon.h> -+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> -+#include <linux/module.h> -+#include <linux/of.h> -+#include <linux/platform_device.h> -+#include <linux/pm_runtime.h> -+#include <linux/regmap.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+#define IPU_PRG_CTL 0x00 -+#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) -+#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 -+#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) -+#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) -+#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) -+#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) -+#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) -+#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) -+#define IPU_PRG_CTL_SOFTRST (1 << 30) -+#define IPU_PRG_CTL_SHADOW_EN (1 << 31) -+ -+#define IPU_PRG_STATUS 0x04 -+#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) -+#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) -+ -+#define IPU_PRG_QOS 0x08 -+#define IPU_PRG_QOS_ARID_MASK 0xf -+#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) -+ -+#define IPU_PRG_REG_UPDATE 0x0c -+#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) -+ -+#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) -+#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff -+ -+#define IPU_PRG_CROP_LINE 0x1c -+ -+#define IPU_PRG_THD 0x20 -+ -+#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) -+ -+#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) -+ -+#define IPU_PRG_ILO(i) (0x3c + i * 0x4) -+ -+#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff -+#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 -+ -+struct ipu_prg_channel { -+ bool enabled; -+ int used_pre; -+}; -+ -+struct ipu_prg { -+ struct list_head list; -+ struct device *dev; -+ int id; -+ -+ void __iomem *regs; -+ struct clk *clk_ipg, *clk_axi; -+ struct regmap *iomuxc_gpr; -+ struct ipu_pre *pres[3]; -+ -+ struct ipu_prg_channel chan[3]; -+}; -+ -+static DEFINE_MUTEX(ipu_prg_list_mutex); -+static LIST_HEAD(ipu_prg_list); -+ -+struct ipu_prg * -+ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id) -+{ -+ struct device_node *prg_node = of_parse_phandle(dev->of_node, -+ name, 0); -+ struct ipu_prg *prg; -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_for_each_entry(prg, &ipu_prg_list, list) { -+ if (prg_node == prg->dev->of_node) { -+ mutex_unlock(&ipu_prg_list_mutex); -+ device_link_add(dev, prg->dev, -+ DL_FLAG_AUTOREMOVE_CONSUMER); -+ prg->id = ipu_id; -+ of_node_put(prg_node); -+ return prg; -+ } -+ } -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ of_node_put(prg_node); -+ -+ return NULL; -+} -+ -+int ipu_prg_max_active_channels(void) -+{ -+ return ipu_pre_get_available_count(); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels); -+ -+bool ipu_prg_present(struct ipu_soc *ipu) -+{ -+ if (ipu->prg_priv) -+ return true; -+ -+ return false; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_present); -+ -+bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, -+ uint64_t modifier) -+{ -+ const struct drm_format_info *info = drm_format_info(format); -+ -+ if (info->num_planes != 1) -+ return false; -+ -+ switch (modifier) { -+ case DRM_FORMAT_MOD_LINEAR: -+ case DRM_FORMAT_MOD_VIVANTE_TILED: -+ case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: -+ return true; -+ default: -+ return false; -+ } -+} -+EXPORT_SYMBOL_GPL(ipu_prg_format_supported); -+ -+int ipu_prg_enable(struct ipu_soc *ipu) -+{ -+ struct ipu_prg *prg = ipu->prg_priv; -+ -+ if (!prg) -+ return 0; -+ -+ return pm_runtime_get_sync(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_enable); -+ -+void ipu_prg_disable(struct ipu_soc *ipu) -+{ -+ struct ipu_prg *prg = ipu->prg_priv; -+ -+ if (!prg) -+ return; -+ -+ pm_runtime_put(prg->dev); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_disable); -+ -+/* -+ * The channel configuartion functions below are not thread safe, as they -+ * must be only called from the atomic commit path in the DRM driver, which -+ * is properly serialized. -+ */ -+static int ipu_prg_ipu_to_prg_chan(int ipu_chan) -+{ -+ /* -+ * This isn't clearly documented in the RM, but IPU to PRG channel -+ * assignment is fixed, as only with this mapping the control signals -+ * match up. -+ */ -+ switch (ipu_chan) { -+ case IPUV3_CHANNEL_MEM_BG_SYNC: -+ return 0; -+ case IPUV3_CHANNEL_MEM_FG_SYNC: -+ return 1; -+ case IPUV3_CHANNEL_MEM_DC_SYNC: -+ return 2; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ int i, ret; -+ -+ /* channel 0 is special as it is hardwired to one of the PREs */ -+ if (prg_chan == 0) { -+ ret = ipu_pre_get(prg->pres[0]); -+ if (ret) -+ goto fail; -+ prg->chan[prg_chan].used_pre = 0; -+ return 0; -+ } -+ -+ for (i = 1; i < 3; i++) { -+ ret = ipu_pre_get(prg->pres[i]); -+ if (!ret) { -+ u32 val, mux; -+ int shift; -+ -+ prg->chan[prg_chan].used_pre = i; -+ -+ /* configure the PRE to PRG channel mux */ -+ shift = (i == 1) ? 12 : 14; -+ mux = (prg->id << 1) | (prg_chan - 1); -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, mux << shift); -+ -+ /* check other mux, must not point to same channel */ -+ shift = (i == 1) ? 14 : 12; -+ regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); -+ if (((val >> shift) & 0x3) == mux) { -+ regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, -+ 0x3 << shift, -+ (mux ^ 0x1) << shift); -+ } -+ -+ return 0; -+ } -+ } -+ -+fail: -+ dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); -+ return ret; -+} -+ -+static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) -+{ -+ struct ipu_prg_channel *chan = &prg->chan[prg_chan]; -+ -+ ipu_pre_put(prg->pres[chan->used_pre]); -+ chan->used_pre = -1; -+} -+ -+void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ -+ if (prg_chan < 0) -+ return; -+ -+ chan = &prg->chan[prg_chan]; -+ if (!chan->enabled) -+ return; -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ pm_runtime_put(prg->dev); -+ -+ ipu_prg_put_pre(prg, prg_chan); -+ -+ chan->enabled = false; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_disable); -+ -+int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, -+ unsigned int axi_id, unsigned int width, -+ unsigned int height, unsigned int stride, -+ u32 format, uint64_t modifier, unsigned long *eba) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ u32 val; -+ int ret; -+ -+ if (prg_chan < 0) -+ return prg_chan; -+ -+ chan = &prg->chan[prg_chan]; -+ -+ if (chan->enabled) { -+ ipu_pre_update(prg->pres[chan->used_pre], *eba); -+ return 0; -+ } -+ -+ ret = ipu_prg_get_pre(prg, prg_chan); -+ if (ret) -+ return ret; -+ -+ ipu_pre_configure(prg->pres[chan->used_pre], -+ width, height, stride, format, modifier, *eba); -+ -+ -+ pm_runtime_get_sync(prg->dev); -+ -+ val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; -+ writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); -+ -+ val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | -+ ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << -+ IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); -+ writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); -+ -+ val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); -+ *eba = val; -+ writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); -+ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ /* config AXI ID */ -+ val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << -+ IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); -+ val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); -+ /* enable channel */ -+ val &= ~IPU_PRG_CTL_BYPASS(prg_chan); -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ val = IPU_PRG_REG_UPDATE_REG_UPDATE; -+ writel(val, prg->regs + IPU_PRG_REG_UPDATE); -+ -+ /* wait for both double buffers to be filled */ -+ readl_poll_timeout(prg->regs + IPU_PRG_STATUS, val, -+ (val & IPU_PRG_STATUS_BUFFER0_READY(prg_chan)) && -+ (val & IPU_PRG_STATUS_BUFFER1_READY(prg_chan)), -+ 5, 1000); -+ -+ pm_runtime_put(prg->dev); -+ -+ chan->enabled = true; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure); -+ -+bool ipu_prg_channel_configure_pending(struct ipuv3_channel *ipu_chan) -+{ -+ int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); -+ struct ipu_prg *prg = ipu_chan->ipu->prg_priv; -+ struct ipu_prg_channel *chan; -+ -+ if (prg_chan < 0) -+ return false; -+ -+ chan = &prg->chan[prg_chan]; -+ WARN_ON(!chan->enabled); -+ -+ return ipu_pre_update_pending(prg->pres[chan->used_pre]); -+} -+EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); -+ -+static int ipu_prg_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct resource *res; -+ struct ipu_prg *prg; -+ u32 val; -+ int i, ret; -+ -+ prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); -+ if (!prg) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ prg->regs = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(prg->regs)) -+ return PTR_ERR(prg->regs); -+ -+ -+ prg->clk_ipg = devm_clk_get(dev, "ipg"); -+ if (IS_ERR(prg->clk_ipg)) -+ return PTR_ERR(prg->clk_ipg); -+ -+ prg->clk_axi = devm_clk_get(dev, "axi"); -+ if (IS_ERR(prg->clk_axi)) -+ return PTR_ERR(prg->clk_axi); -+ -+ prg->iomuxc_gpr = -+ syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); -+ if (IS_ERR(prg->iomuxc_gpr)) -+ return PTR_ERR(prg->iomuxc_gpr); -+ -+ for (i = 0; i < 3; i++) { -+ prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i); -+ if (!prg->pres[i]) -+ return -EPROBE_DEFER; -+ } -+ -+ ret = clk_prepare_enable(prg->clk_ipg); -+ if (ret) -+ return ret; -+ -+ ret = clk_prepare_enable(prg->clk_axi); -+ if (ret) { -+ clk_disable_unprepare(prg->clk_ipg); -+ return ret; -+ } -+ -+ /* init to free running mode */ -+ val = readl(prg->regs + IPU_PRG_CTL); -+ val |= IPU_PRG_CTL_SHADOW_EN; -+ writel(val, prg->regs + IPU_PRG_CTL); -+ -+ /* disable address threshold */ -+ writel(0xffffffff, prg->regs + IPU_PRG_THD); -+ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ -+ prg->dev = dev; -+ platform_set_drvdata(pdev, prg); -+ mutex_lock(&ipu_prg_list_mutex); -+ list_add(&prg->list, &ipu_prg_list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ return 0; -+} -+ -+static int ipu_prg_remove(struct platform_device *pdev) -+{ -+ struct ipu_prg *prg = platform_get_drvdata(pdev); -+ -+ mutex_lock(&ipu_prg_list_mutex); -+ list_del(&prg->list); -+ mutex_unlock(&ipu_prg_list_mutex); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int prg_suspend(struct device *dev) -+{ -+ struct ipu_prg *prg = dev_get_drvdata(dev); -+ -+ clk_disable_unprepare(prg->clk_axi); -+ clk_disable_unprepare(prg->clk_ipg); -+ -+ return 0; -+} -+ -+static int prg_resume(struct device *dev) -+{ -+ struct ipu_prg *prg = dev_get_drvdata(dev); -+ int ret; -+ -+ ret = clk_prepare_enable(prg->clk_ipg); -+ if (ret) -+ return ret; -+ -+ ret = clk_prepare_enable(prg->clk_axi); -+ if (ret) { -+ clk_disable_unprepare(prg->clk_ipg); -+ return ret; -+ } -+ -+ return 0; -+} -+#endif -+ -+static const struct dev_pm_ops prg_pm_ops = { -+ SET_RUNTIME_PM_OPS(prg_suspend, prg_resume, NULL) -+}; -+ -+static const struct of_device_id ipu_prg_dt_ids[] = { -+ { .compatible = "fsl,imx6qp-prg", }, -+ { /* sentinel */ }, -+}; -+ -+struct platform_driver ipu_prg_drv = { -+ .probe = ipu_prg_probe, -+ .remove = ipu_prg_remove, -+ .driver = { -+ .name = "imx-ipu-prg", -+ .pm = &prg_pm_ops, -+ .of_match_table = ipu_prg_dt_ids, -+ }, -+}; ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-prv.h -@@ -0,0 +1,274 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+/* -+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#ifndef __IPU_PRV_H__ -+#define __IPU_PRV_H__ -+ -+struct ipu_soc; -+ -+#include <linux/types.h> -+#include <linux/device.h> -+#include <linux/clk.h> -+#include <linux/platform_device.h> -+ -+#include <video/imx-ipu-v3.h> -+ -+#define IPU_MCU_T_DEFAULT 8 -+#define IPU_CM_IDMAC_REG_OFS 0x00008000 -+#define IPU_CM_IC_REG_OFS 0x00020000 -+#define IPU_CM_IRT_REG_OFS 0x00028000 -+#define IPU_CM_CSI0_REG_OFS 0x00030000 -+#define IPU_CM_CSI1_REG_OFS 0x00038000 -+#define IPU_CM_SMFC_REG_OFS 0x00050000 -+#define IPU_CM_DC_REG_OFS 0x00058000 -+#define IPU_CM_DMFC_REG_OFS 0x00060000 -+ -+/* Register addresses */ -+/* IPU Common registers */ -+#define IPU_CM_REG(offset) (offset) -+ -+#define IPU_CONF IPU_CM_REG(0) -+ -+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0) -+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4) -+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8) -+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac) -+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0) -+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4) -+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8) -+#define IPU_SKIP IPU_CM_REG(0x00bc) -+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0) -+#define IPU_DISP_GEN IPU_CM_REG(0x00c4) -+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8) -+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc) -+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0) -+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4) -+#define IPU_SNOOP IPU_CM_REG(0x00d8) -+#define IPU_MEM_RST IPU_CM_REG(0x00dc) -+#define IPU_PM IPU_CM_REG(0x00e0) -+#define IPU_GPR IPU_CM_REG(0x00e4) -+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32)) -+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32)) -+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244) -+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248) -+#define IPU_SRM_STAT IPU_CM_REG(0x024C) -+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250) -+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254) -+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32)) -+#define IPU_CHA_BUF2_RDY(ch) IPU_CM_REG(0x0288 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32)) -+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32)) -+ -+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n)) -+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n)) -+ -+/* SRM_PRI2 */ -+#define DP_S_SRM_MODE_MASK (0x3 << 3) -+#define DP_S_SRM_MODE_NOW (0x3 << 3) -+#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3) -+ -+/* FS_PROC_FLOW1 */ -+#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0) -+#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0) -+#define FS_PRPVF_ROT_SRC_SEL_MASK (0xf << 8) -+#define FS_PRPVF_ROT_SRC_SEL_VF (0x8 << 8) -+#define FS_PP_SRC_SEL_MASK (0xf << 12) -+#define FS_PP_ROT_SRC_SEL_MASK (0xf << 16) -+#define FS_PP_ROT_SRC_SEL_PP (0x5 << 16) -+#define FS_VDI1_SRC_SEL_MASK (0x3 << 20) -+#define FS_VDI3_SRC_SEL_MASK (0x3 << 20) -+#define FS_PRP_SRC_SEL_MASK (0xf << 24) -+#define FS_VDI_SRC_SEL_MASK (0x3 << 28) -+#define FS_VDI_SRC_SEL_CSI_DIRECT (0x1 << 28) -+#define FS_VDI_SRC_SEL_VDOA (0x2 << 28) -+ -+/* FS_PROC_FLOW2 */ -+#define FS_PRP_ENC_DEST_SEL_MASK (0xf << 0) -+#define FS_PRP_ENC_DEST_SEL_IRT_ENC (0x1 << 0) -+#define FS_PRPVF_DEST_SEL_MASK (0xf << 4) -+#define FS_PRPVF_DEST_SEL_IRT_VF (0x1 << 4) -+#define FS_PRPVF_ROT_DEST_SEL_MASK (0xf << 8) -+#define FS_PP_DEST_SEL_MASK (0xf << 12) -+#define FS_PP_DEST_SEL_IRT_PP (0x3 << 12) -+#define FS_PP_ROT_DEST_SEL_MASK (0xf << 16) -+#define FS_PRPENC_ROT_DEST_SEL_MASK (0xf << 20) -+#define FS_PRP_DEST_SEL_MASK (0xf << 24) -+ -+#define IPU_DI0_COUNTER_RELEASE (1 << 24) -+#define IPU_DI1_COUNTER_RELEASE (1 << 25) -+ -+#define IPU_IDMAC_REG(offset) (offset) -+ -+#define IDMAC_CONF IPU_IDMAC_REG(0x0000) -+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32)) -+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c) -+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010) -+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32)) -+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32)) -+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024) -+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028) -+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c) -+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030) -+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034) -+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32)) -+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32)) -+ -+#define IPU_NUM_IRQS (32 * 15) -+ -+enum ipu_modules { -+ IPU_CONF_CSI0_EN = (1 << 0), -+ IPU_CONF_CSI1_EN = (1 << 1), -+ IPU_CONF_IC_EN = (1 << 2), -+ IPU_CONF_ROT_EN = (1 << 3), -+ IPU_CONF_ISP_EN = (1 << 4), -+ IPU_CONF_DP_EN = (1 << 5), -+ IPU_CONF_DI0_EN = (1 << 6), -+ IPU_CONF_DI1_EN = (1 << 7), -+ IPU_CONF_SMFC_EN = (1 << 8), -+ IPU_CONF_DC_EN = (1 << 9), -+ IPU_CONF_DMFC_EN = (1 << 10), -+ -+ IPU_CONF_VDI_EN = (1 << 12), -+ -+ IPU_CONF_IDMAC_DIS = (1 << 22), -+ -+ IPU_CONF_IC_DMFC_SEL = (1 << 25), -+ IPU_CONF_IC_DMFC_SYNC = (1 << 26), -+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27), -+ -+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28), -+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29), -+ IPU_CONF_IC_INPUT = (1 << 30), -+ IPU_CONF_CSI_SEL = (1 << 31), -+}; -+ -+struct ipuv3_channel { -+ unsigned int num; -+ struct ipu_soc *ipu; -+ struct list_head list; -+}; -+ -+struct ipu_cpmem; -+struct ipu_csi; -+struct ipu_dc_priv; -+struct ipu_dmfc_priv; -+struct ipu_di; -+struct ipu_ic_priv; -+struct ipu_vdi; -+struct ipu_image_convert_priv; -+struct ipu_smfc_priv; -+struct ipu_pre; -+struct ipu_prg; -+ -+struct ipu_devtype; -+ -+struct ipu_soc { -+ struct device *dev; -+ const struct ipu_devtype *devtype; -+ enum ipuv3_type ipu_type; -+ spinlock_t lock; -+ struct mutex channel_lock; -+ struct list_head channels; -+ -+ void __iomem *cm_reg; -+ void __iomem *idmac_reg; -+ -+ int id; -+ int usecount; -+ -+ struct clk *clk; -+ -+ int irq_sync; -+ int irq_err; -+ struct irq_domain *domain; -+ -+ struct ipu_cpmem *cpmem_priv; -+ struct ipu_dc_priv *dc_priv; -+ struct ipu_dp_priv *dp_priv; -+ struct ipu_dmfc_priv *dmfc_priv; -+ struct ipu_di *di_priv[2]; -+ struct ipu_csi *csi_priv[2]; -+ struct ipu_ic_priv *ic_priv; -+ struct ipu_vdi *vdi_priv; -+ struct ipu_image_convert_priv *image_convert_priv; -+ struct ipu_smfc_priv *smfc_priv; -+ struct ipu_prg *prg_priv; -+}; -+ -+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) -+{ -+ return readl(ipu->idmac_reg + offset); -+} -+ -+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value, -+ unsigned offset) -+{ -+ writel(value, ipu->idmac_reg + offset); -+} -+ -+void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync); -+ -+int ipu_module_enable(struct ipu_soc *ipu, u32 mask); -+int ipu_module_disable(struct ipu_soc *ipu, u32 mask); -+ -+bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno); -+ -+int ipu_csi_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *clk_ipu); -+void ipu_csi_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_ic_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, unsigned long tpmem_base); -+void ipu_ic_exit(struct ipu_soc *ipu); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module); -+void ipu_vdi_exit(struct ipu_soc *ipu); -+ -+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev); -+void ipu_image_convert_exit(struct ipu_soc *ipu); -+ -+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, -+ unsigned long base, u32 module, struct clk *ipu_clk); -+void ipu_di_exit(struct ipu_soc *ipu, int id); -+ -+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ struct clk *ipu_clk); -+void ipu_dmfc_exit(struct ipu_soc *ipu); -+ -+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_dp_exit(struct ipu_soc *ipu); -+ -+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base, -+ unsigned long template_base); -+void ipu_dc_exit(struct ipu_soc *ipu); -+ -+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_cpmem_exit(struct ipu_soc *ipu); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); -+void ipu_smfc_exit(struct ipu_soc *ipu); -+ -+struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name, -+ int index); -+int ipu_pre_get_available_count(void); -+int ipu_pre_get(struct ipu_pre *pre); -+void ipu_pre_put(struct ipu_pre *pre); -+u32 ipu_pre_get_baddr(struct ipu_pre *pre); -+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, -+ unsigned int height, unsigned int stride, u32 format, -+ uint64_t modifier, unsigned int bufaddr); -+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); -+bool ipu_pre_update_pending(struct ipu_pre *pre); -+ -+struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name, -+ int ipu_id); -+ -+extern struct platform_driver ipu_pre_drv; -+extern struct platform_driver ipu_prg_drv; -+ -+#endif /* __IPU_PRV_H__ */ ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-smfc.c -@@ -0,0 +1,202 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. -+ */ -+#include <linux/export.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/errno.h> -+#include <linux/spinlock.h> -+#include <linux/delay.h> -+#include <linux/clk.h> -+#include <video/imx-ipu-v3.h> -+ -+#include "ipu-prv.h" -+ -+struct ipu_smfc { -+ struct ipu_smfc_priv *priv; -+ int chno; -+ bool inuse; -+}; -+ -+struct ipu_smfc_priv { -+ void __iomem *base; -+ spinlock_t lock; -+ struct ipu_soc *ipu; -+ struct ipu_smfc channel[4]; -+ int use_count; -+}; -+ -+/*SMFC Registers */ -+#define SMFC_MAP 0x0000 -+#define SMFC_WMC 0x0004 -+#define SMFC_BS 0x0008 -+ -+int ipu_smfc_set_burstsize(struct ipu_smfc *smfc, int burstsize) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 4; -+ val = readl(priv->base + SMFC_BS); -+ val &= ~(0xf << shift); -+ val |= burstsize << shift; -+ writel(val, priv->base + SMFC_BS); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_burstsize); -+ -+int ipu_smfc_map_channel(struct ipu_smfc *smfc, int csi_id, int mipi_id) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 3; -+ val = readl(priv->base + SMFC_MAP); -+ val &= ~(0x7 << shift); -+ val |= ((csi_id << 2) | mipi_id) << shift; -+ writel(val, priv->base + SMFC_MAP); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_map_channel); -+ -+int ipu_smfc_set_watermark(struct ipu_smfc *smfc, u32 set_level, u32 clr_level) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ u32 val, shift; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ shift = smfc->chno * 6 + (smfc->chno > 1 ? 4 : 0); -+ val = readl(priv->base + SMFC_WMC); -+ val &= ~(0x3f << shift); -+ val |= ((clr_level << 3) | set_level) << shift; -+ writel(val, priv->base + SMFC_WMC); -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_set_watermark); -+ -+int ipu_smfc_enable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (!priv->use_count) -+ ipu_module_enable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ priv->use_count++; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_enable); -+ -+int ipu_smfc_disable(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ priv->use_count--; -+ -+ if (!priv->use_count) -+ ipu_module_disable(priv->ipu, IPU_CONF_SMFC_EN); -+ -+ if (priv->use_count < 0) -+ priv->use_count = 0; -+ -+ spin_unlock_irqrestore(&priv->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_disable); -+ -+struct ipu_smfc *ipu_smfc_get(struct ipu_soc *ipu, unsigned int chno) -+{ -+ struct ipu_smfc_priv *priv = ipu->smfc_priv; -+ struct ipu_smfc *smfc, *ret; -+ unsigned long flags; -+ -+ if (chno >= 4) -+ return ERR_PTR(-EINVAL); -+ -+ smfc = &priv->channel[chno]; -+ ret = smfc; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ -+ if (smfc->inuse) { -+ ret = ERR_PTR(-EBUSY); -+ goto unlock; -+ } -+ -+ smfc->inuse = true; -+unlock: -+ spin_unlock_irqrestore(&priv->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_get); -+ -+void ipu_smfc_put(struct ipu_smfc *smfc) -+{ -+ struct ipu_smfc_priv *priv = smfc->priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&priv->lock, flags); -+ smfc->inuse = false; -+ spin_unlock_irqrestore(&priv->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_smfc_put); -+ -+int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base) -+{ -+ struct ipu_smfc_priv *priv; -+ int i; -+ -+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ ipu->smfc_priv = priv; -+ spin_lock_init(&priv->lock); -+ priv->ipu = ipu; -+ -+ priv->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!priv->base) -+ return -ENOMEM; -+ -+ for (i = 0; i < 4; i++) { -+ priv->channel[i].priv = priv; -+ priv->channel[i].chno = i; -+ } -+ -+ pr_debug("%s: ioremap 0x%08lx -> %p\n", __func__, base, priv->base); -+ -+ return 0; -+} -+ -+void ipu_smfc_exit(struct ipu_soc *ipu) -+{ -+} ---- /dev/null -+++ b/drivers/gpu/ipu-v3/ipu-vdi.c -@@ -0,0 +1,234 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Copyright (C) 2012-2016 Mentor Graphics Inc. -+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc. -+ */ -+#include <linux/io.h> -+#include "ipu-prv.h" -+ -+struct ipu_vdi { -+ void __iomem *base; -+ u32 module; -+ spinlock_t lock; -+ int use_count; -+ struct ipu_soc *ipu; -+}; -+ -+ -+/* VDI Register Offsets */ -+#define VDI_FSIZE 0x0000 -+#define VDI_C 0x0004 -+ -+/* VDI Register Fields */ -+#define VDI_C_CH_420 (0 << 1) -+#define VDI_C_CH_422 (1 << 1) -+#define VDI_C_MOT_SEL_MASK (0x3 << 2) -+#define VDI_C_MOT_SEL_FULL (2 << 2) -+#define VDI_C_MOT_SEL_LOW (1 << 2) -+#define VDI_C_MOT_SEL_MED (0 << 2) -+#define VDI_C_BURST_SIZE1_4 (3 << 4) -+#define VDI_C_BURST_SIZE2_4 (3 << 8) -+#define VDI_C_BURST_SIZE3_4 (3 << 12) -+#define VDI_C_BURST_SIZE_MASK 0xF -+#define VDI_C_BURST_SIZE1_OFFSET 4 -+#define VDI_C_BURST_SIZE2_OFFSET 8 -+#define VDI_C_BURST_SIZE3_OFFSET 12 -+#define VDI_C_VWM1_SET_1 (0 << 16) -+#define VDI_C_VWM1_SET_2 (1 << 16) -+#define VDI_C_VWM1_CLR_2 (1 << 19) -+#define VDI_C_VWM3_SET_1 (0 << 22) -+#define VDI_C_VWM3_SET_2 (1 << 22) -+#define VDI_C_VWM3_CLR_2 (1 << 25) -+#define VDI_C_TOP_FIELD_MAN_1 (1 << 30) -+#define VDI_C_TOP_FIELD_AUTO_1 (1 << 31) -+ -+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset) -+{ -+ return readl(vdi->base + offset); -+} -+ -+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value, -+ unsigned int offset) -+{ -+ writel(value, vdi->base + offset); -+} -+ -+void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field) -+{ -+ bool top_field_0 = false; -+ unsigned long flags; -+ u32 reg; -+ -+ switch (field) { -+ case V4L2_FIELD_INTERLACED_TB: -+ case V4L2_FIELD_SEQ_TB: -+ case V4L2_FIELD_TOP: -+ top_field_0 = true; -+ break; -+ case V4L2_FIELD_INTERLACED_BT: -+ case V4L2_FIELD_SEQ_BT: -+ case V4L2_FIELD_BOTTOM: -+ top_field_0 = false; -+ break; -+ default: -+ top_field_0 = (std & V4L2_STD_525_60) ? true : false; -+ break; -+ } -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ if (top_field_0) -+ reg &= ~(VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1); -+ else -+ reg |= VDI_C_TOP_FIELD_MAN_1 | VDI_C_TOP_FIELD_AUTO_1; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order); -+ -+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel) -+{ -+ unsigned long flags; -+ u32 reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ -+ reg &= ~VDI_C_MOT_SEL_MASK; -+ -+ switch (motion_sel) { -+ case MED_MOTION: -+ reg |= VDI_C_MOT_SEL_MED; -+ break; -+ case HIGH_MOTION: -+ reg |= VDI_C_MOT_SEL_FULL; -+ break; -+ default: -+ reg |= VDI_C_MOT_SEL_LOW; -+ break; -+ } -+ -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_set_motion); -+ -+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres) -+{ -+ unsigned long flags; -+ u32 pixel_fmt, reg; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ reg = ((yres - 1) << 16) | (xres - 1); -+ ipu_vdi_write(vdi, reg, VDI_FSIZE); -+ -+ /* -+ * Full motion, only vertical filter is used. -+ * Burst size is 4 accesses -+ */ -+ if (code == MEDIA_BUS_FMT_UYVY8_2X8 || -+ code == MEDIA_BUS_FMT_UYVY8_1X16 || -+ code == MEDIA_BUS_FMT_YUYV8_2X8 || -+ code == MEDIA_BUS_FMT_YUYV8_1X16) -+ pixel_fmt = VDI_C_CH_422; -+ else -+ pixel_fmt = VDI_C_CH_420; -+ -+ reg = ipu_vdi_read(vdi, VDI_C); -+ reg |= pixel_fmt; -+ reg |= VDI_C_BURST_SIZE2_4; -+ reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2; -+ reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2; -+ ipu_vdi_write(vdi, reg, VDI_C); -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_setup); -+ -+void ipu_vdi_unsetup(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ ipu_vdi_write(vdi, 0, VDI_FSIZE); -+ ipu_vdi_write(vdi, 0, VDI_C); -+ spin_unlock_irqrestore(&vdi->lock, flags); -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup); -+ -+int ipu_vdi_enable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (!vdi->use_count) -+ ipu_module_enable(vdi->ipu, vdi->module); -+ -+ vdi->use_count++; -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_enable); -+ -+int ipu_vdi_disable(struct ipu_vdi *vdi) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&vdi->lock, flags); -+ -+ if (vdi->use_count) { -+ if (!--vdi->use_count) -+ ipu_module_disable(vdi->ipu, vdi->module); -+ } -+ -+ spin_unlock_irqrestore(&vdi->lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_disable); -+ -+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu) -+{ -+ return ipu->vdi_priv; -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_get); -+ -+void ipu_vdi_put(struct ipu_vdi *vdi) -+{ -+} -+EXPORT_SYMBOL_GPL(ipu_vdi_put); -+ -+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev, -+ unsigned long base, u32 module) -+{ -+ struct ipu_vdi *vdi; -+ -+ vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL); -+ if (!vdi) -+ return -ENOMEM; -+ -+ ipu->vdi_priv = vdi; -+ -+ spin_lock_init(&vdi->lock); -+ vdi->module = module; -+ vdi->base = devm_ioremap(dev, base, PAGE_SIZE); -+ if (!vdi->base) -+ return -ENOMEM; -+ -+ dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base); -+ vdi->ipu = ipu; -+ -+ return 0; -+} -+ -+void ipu_vdi_exit(struct ipu_soc *ipu) -+{ -+} ---- a/drivers/video/Kconfig -+++ b/drivers/video/Kconfig -@@ -15,7 +15,7 @@ source "drivers/char/agp/Kconfig" - source "drivers/gpu/vga/Kconfig" - - source "drivers/gpu/host1x/Kconfig" --source "drivers/gpu/imx/Kconfig" -+source "drivers/gpu/ipu-v3/Kconfig" - - source "drivers/gpu/drm/Kconfig" - diff --git a/target/linux/layerscape/patches-5.4/805-display-0050-Revert-drm-imx-Revert-a-patch-which-merges-imx-drm-c.patch b/target/linux/layerscape/patches-5.4/805-display-0050-Revert-drm-imx-Revert-a-patch-which-merges-imx-drm-c.patch deleted file mode 100644 index c04aede833..0000000000 --- a/target/linux/layerscape/patches-5.4/805-display-0050-Revert-drm-imx-Revert-a-patch-which-merges-imx-drm-c.patch +++ /dev/null @@ -1,107 +0,0 @@ -From c0dd62d0bc48c0d9e833116ea2696cc000633897 Mon Sep 17 00:00:00 2001 -From: Yangbo Lu <yangbo.lu@nxp.com> -Date: Mon, 2 Mar 2020 13:46:39 +0800 -Subject: [PATCH] Revert "drm/imx: Revert a patch which merges imx-drm-core and - ipuv3-crtc in one module" - -This reverts commit 1e7f3c4066d9afb4bc036b9d4b6f5b18f3c918c7. ---- - drivers/gpu/drm/imx/Kconfig | 7 ------- - drivers/gpu/drm/imx/Makefile | 4 +--- - drivers/gpu/drm/imx/imx-drm-core.c | 18 +++++++++++++++++- - drivers/gpu/drm/imx/imx-drm.h | 2 ++ - drivers/gpu/drm/imx/ipuv3-crtc.c | 8 +------- - 5 files changed, 21 insertions(+), 18 deletions(-) - ---- a/drivers/gpu/drm/imx/Kconfig -+++ b/drivers/gpu/drm/imx/Kconfig -@@ -33,13 +33,6 @@ config DRM_IMX_LDB - Choose this to enable the internal LVDS Display Bridge (LDB) - found on i.MX53 and i.MX6 processors. - --config DRM_IMX_IPUV3 -- tristate -- depends on DRM_IMX -- depends on IMX_IPUV3_CORE -- default y if DRM_IMX=y -- default m if DRM_IMX=m -- - config DRM_IMX_HDMI - tristate "Freescale i.MX DRM HDMI" - select DRM_DW_HDMI ---- a/drivers/gpu/drm/imx/Makefile -+++ b/drivers/gpu/drm/imx/Makefile -@@ -1,6 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0 - --imxdrm-objs := imx-drm-core.o -+imxdrm-objs := imx-drm-core.o ipuv3-crtc.o ipuv3-plane.o - - obj-$(CONFIG_DRM_IMX) += imxdrm.o - -@@ -8,7 +8,5 @@ obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) + - obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o - obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o - --imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o --obj-$(CONFIG_DRM_IMX_IPUV3) += imx-ipuv3-crtc.o - obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o - obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdn-mhdp-imxdrv.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o cdn-mhdp-imx8qm.o cdn-mhdp-ls1028a.o ---- a/drivers/gpu/drm/imx/imx-drm-core.c -+++ b/drivers/gpu/drm/imx/imx-drm-core.c -@@ -361,7 +361,23 @@ static struct platform_driver imx_drm_pd - .of_match_table = imx_drm_dt_ids, - }, - }; --module_platform_driver(imx_drm_pdrv); -+ -+static struct platform_driver * const drivers[] = { -+ &imx_drm_pdrv, -+ &ipu_drm_driver, -+}; -+ -+static int __init imx_drm_init(void) -+{ -+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_init(imx_drm_init); -+ -+static void __exit imx_drm_exit(void) -+{ -+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); -+} -+module_exit(imx_drm_exit); - - MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); - MODULE_DESCRIPTION("i.MX drm driver core"); ---- a/drivers/gpu/drm/imx/imx-drm.h -+++ b/drivers/gpu/drm/imx/imx-drm.h -@@ -28,6 +28,8 @@ int imx_drm_init_drm(struct platform_dev - int preferred_bpp); - int imx_drm_exit_drm(void); - -+extern struct platform_driver ipu_drm_driver; -+ - void imx_drm_mode_config_init(struct drm_device *drm); - - struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); ---- a/drivers/gpu/drm/imx/ipuv3-crtc.c -+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c -@@ -492,16 +492,10 @@ static int ipu_drm_remove(struct platfor - return 0; - } - --static struct platform_driver ipu_drm_driver = { -+struct platform_driver ipu_drm_driver = { - .driver = { - .name = "imx-ipuv3-crtc", - }, - .probe = ipu_drm_probe, - .remove = ipu_drm_remove, - }; --module_platform_driver(ipu_drm_driver); -- --MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); --MODULE_DESCRIPTION(DRIVER_DESC); --MODULE_LICENSE("GPL"); --MODULE_ALIAS("platform:imx-ipuv3-crtc"); |